Merge "Pause webkit painting when UI tile painting queue is full"
diff --git a/Android.mk b/Android.mk
index ace7b7c..5acfb86 100644
--- a/Android.mk
+++ b/Android.mk
@@ -94,7 +94,7 @@
core/java/android/bluetooth/IBluetoothHealthCallback.aidl \
core/java/android/bluetooth/IBluetoothPbap.aidl \
core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl \
- core/java/android/content/ICancelationSignal.aidl \
+ core/java/android/content/ICancellationSignal.aidl \
core/java/android/content/IClipboard.aidl \
core/java/android/content/IContentService.aidl \
core/java/android/content/IIntentReceiver.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index fb334fc..d74d7b1 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -117,6 +117,8 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/fonts/DroidSans*)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/content)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/src/android/content)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
diff --git a/api/current.txt b/api/current.txt
index d0176a5..574af6b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2349,6 +2349,15 @@
method public void setPropertyName(java.lang.String);
}
+ public class TimeAnimator extends android.animation.ValueAnimator {
+ ctor public TimeAnimator();
+ method public void setTimeListener(android.animation.TimeAnimator.TimeListener);
+ }
+
+ public static abstract interface TimeAnimator.TimeListener {
+ method public abstract void onTimeUpdate(android.animation.TimeAnimator, long, long);
+ }
+
public abstract interface TimeInterpolator {
method public abstract float getInterpolation(float);
}
@@ -4670,10 +4679,9 @@
public abstract class AsyncTaskLoader extends android.content.Loader {
ctor public AsyncTaskLoader(android.content.Context);
- method public boolean cancelLoad();
- method protected boolean isLoadInBackgroundCanceled();
+ method public void cancelLoadInBackground();
+ method public boolean isLoadInBackgroundCanceled();
method public abstract D loadInBackground();
- method protected void onCancelLoadInBackground();
method public void onCanceled(D);
method protected D onLoadInBackground();
method public void setUpdateThrottle(long);
@@ -4715,15 +4723,15 @@
method public final void setResultExtras(android.os.Bundle);
}
- public final class CancelationSignal {
- ctor public CancelationSignal();
+ public final class CancellationSignal {
+ ctor public CancellationSignal();
method public void cancel();
method public boolean isCanceled();
- method public void setOnCancelListener(android.content.CancelationSignal.OnCancelListener);
+ method public void setOnCancelListener(android.content.CancellationSignal.OnCancelListener);
method public void throwIfCanceled();
}
- public static abstract interface CancelationSignal.OnCancelListener {
+ public static abstract interface CancellationSignal.OnCancelListener {
method public abstract void onCancel();
}
@@ -4846,7 +4854,7 @@
method public android.os.ParcelFileDescriptor openPipeHelper(android.net.Uri, java.lang.String, android.os.Bundle, T, android.content.ContentProvider.PipeDataWriter<T>) throws java.io.FileNotFoundException;
method public android.content.res.AssetFileDescriptor openTypedAssetFile(android.net.Uri, java.lang.String, android.os.Bundle) throws java.io.FileNotFoundException;
method public abstract android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
- method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.content.CancelationSignal);
+ method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.content.CancellationSignal);
method protected final void setPathPermissions(android.content.pm.PathPermission[]);
method protected final void setReadPermission(java.lang.String);
method protected final void setWritePermission(java.lang.String);
@@ -4870,7 +4878,7 @@
method public android.os.ParcelFileDescriptor openFile(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException, android.os.RemoteException;
method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle) throws java.io.FileNotFoundException, android.os.RemoteException;
method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String) throws android.os.RemoteException;
- method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.content.CancelationSignal) throws android.os.RemoteException;
+ method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.content.CancellationSignal) throws android.os.RemoteException;
method public boolean release();
method public int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]) throws android.os.RemoteException;
}
@@ -4957,7 +4965,7 @@
method public final java.io.OutputStream openOutputStream(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle) throws java.io.FileNotFoundException;
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
- method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.content.CancelationSignal);
+ method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.content.CancellationSignal);
method public final void registerContentObserver(android.net.Uri, boolean, android.database.ContentObserver);
method public static void removePeriodicSync(android.accounts.Account, java.lang.String, android.os.Bundle);
method public static void removeStatusChangeListener(java.lang.Object);
@@ -5765,7 +5773,9 @@
public class Loader {
ctor public Loader(android.content.Context);
method public void abandon();
+ method public boolean cancelLoad();
method public java.lang.String dataToString(D);
+ method public void deliverCancellation();
method public void deliverResult(D);
method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
method public void forceLoad();
@@ -5775,23 +5785,30 @@
method public boolean isReset();
method public boolean isStarted();
method protected void onAbandon();
+ method protected boolean onCancelLoad();
method public void onContentChanged();
method protected void onForceLoad();
method protected void onReset();
method protected void onStartLoading();
method protected void onStopLoading();
method public void registerListener(int, android.content.Loader.OnLoadCompleteListener<D>);
+ method public void registerOnLoadCanceledListener(android.content.Loader.OnLoadCanceledListener<D>);
method public void reset();
method public final void startLoading();
method public void stopLoading();
method public boolean takeContentChanged();
method public void unregisterListener(android.content.Loader.OnLoadCompleteListener<D>);
+ method public void unregisterOnLoadCanceledListener(android.content.Loader.OnLoadCanceledListener<D>);
}
public final class Loader.ForceLoadContentObserver extends android.database.ContentObserver {
ctor public Loader.ForceLoadContentObserver();
}
+ public static abstract interface Loader.OnLoadCanceledListener {
+ method public abstract void onLoadCanceled(android.content.Loader<D>);
+ }
+
public static abstract interface Loader.OnLoadCompleteListener {
method public abstract void onLoadComplete(android.content.Loader<D>, D);
}
@@ -7260,15 +7277,15 @@
method public static android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory);
method public static android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, android.database.DatabaseErrorHandler);
method public android.database.Cursor query(boolean, java.lang.String, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String);
- method public android.database.Cursor query(boolean, java.lang.String, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String, android.content.CancelationSignal);
+ method public android.database.Cursor query(boolean, java.lang.String, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String, android.content.CancellationSignal);
method public android.database.Cursor query(java.lang.String, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String);
method public android.database.Cursor query(java.lang.String, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String);
method public android.database.Cursor queryWithFactory(android.database.sqlite.SQLiteDatabase.CursorFactory, boolean, java.lang.String, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String);
- method public android.database.Cursor queryWithFactory(android.database.sqlite.SQLiteDatabase.CursorFactory, boolean, java.lang.String, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String, android.content.CancelationSignal);
+ method public android.database.Cursor queryWithFactory(android.database.sqlite.SQLiteDatabase.CursorFactory, boolean, java.lang.String, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String, android.content.CancellationSignal);
method public android.database.Cursor rawQuery(java.lang.String, java.lang.String[]);
- method public android.database.Cursor rawQuery(java.lang.String, java.lang.String[], android.content.CancelationSignal);
+ method public android.database.Cursor rawQuery(java.lang.String, java.lang.String[], android.content.CancellationSignal);
method public android.database.Cursor rawQueryWithFactory(android.database.sqlite.SQLiteDatabase.CursorFactory, java.lang.String, java.lang.String[], java.lang.String);
- method public android.database.Cursor rawQueryWithFactory(android.database.sqlite.SQLiteDatabase.CursorFactory, java.lang.String, java.lang.String[], java.lang.String, android.content.CancelationSignal);
+ method public android.database.Cursor rawQueryWithFactory(android.database.sqlite.SQLiteDatabase.CursorFactory, java.lang.String, java.lang.String[], java.lang.String, android.content.CancellationSignal);
method public static int releaseMemory();
method public long replace(java.lang.String, java.lang.String, android.content.ContentValues);
method public long replaceOrThrow(java.lang.String, java.lang.String, android.content.ContentValues) throws android.database.SQLException;
@@ -7390,7 +7407,7 @@
method public java.lang.String getTables();
method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String);
method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String);
- method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String, android.content.CancelationSignal);
+ method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String, android.content.CancellationSignal);
method public void setCursorFactory(android.database.sqlite.SQLiteDatabase.CursorFactory);
method public void setDistinct(boolean);
method public void setProjectionMap(java.util.Map<java.lang.String, java.lang.String>);
@@ -10918,11 +10935,12 @@
}
public final class MediaRecorder.OutputFormat {
+ field public static final int AAC_ADTS = 6; // 0x6
field public static final int AMR_NB = 3; // 0x3
field public static final int AMR_WB = 4; // 0x4
field public static final int DEFAULT = 0; // 0x0
field public static final int MPEG_4 = 2; // 0x2
- field public static final int RAW_AMR = 3; // 0x3
+ field public static final deprecated int RAW_AMR = 3; // 0x3
field public static final int THREE_GPP = 1; // 0x1
}
@@ -11631,7 +11649,7 @@
method public void setNetworkPreference(int);
method public int startUsingNetworkFeature(int, java.lang.String);
method public int stopUsingNetworkFeature(int, java.lang.String);
- field public static final java.lang.String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
+ field public static final deprecated java.lang.String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
field public static final java.lang.String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
field public static final int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1
field public static final java.lang.String EXTRA_EXTRA_INFO = "extraInfo";
@@ -17497,6 +17515,7 @@
field public static final android.net.Uri CONTENT_URI;
field public static final java.lang.String DATA_ROAMING = "data_roaming";
field public static final java.lang.String DEFAULT_INPUT_METHOD = "default_input_method";
+ field public static final java.lang.String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
field public static final java.lang.String DEVICE_PROVISIONED = "device_provisioned";
field public static final java.lang.String ENABLED_ACCESSIBILITY_SERVICES = "enabled_accessibility_services";
field public static final java.lang.String ENABLED_INPUT_METHODS = "enabled_input_methods";
@@ -17575,6 +17594,7 @@
field public static final java.lang.String ALARM_ALERT = "alarm_alert";
field public static final java.lang.String ALWAYS_FINISH_ACTIVITIES = "always_finish_activities";
field public static final deprecated java.lang.String ANDROID_ID = "android_id";
+ field public static final java.lang.String ANIMATOR_DURATION_SCALE = "animator_duration_scale";
field public static final java.lang.String APPEND_FOR_LAST_AUDIBLE = "_last_audible";
field public static final java.lang.String AUTO_TIME = "auto_time";
field public static final java.lang.String AUTO_TIME_ZONE = "auto_time_zone";
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index fddb429..3d36ebf 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -31,6 +31,7 @@
import android.content.pm.IPackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
+import android.os.Binder;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -135,6 +136,8 @@
runToUri(false);
} else if (op.equals("to-intent-uri")) {
runToUri(true);
+ } else if (op.equals("switch-profile")) {
+ runSwitchUser();
} else {
throw new IllegalArgumentException("Unknown command: " + op);
}
@@ -531,7 +534,8 @@
Intent intent = makeIntent();
IntentReceiver receiver = new IntentReceiver();
System.out.println("Broadcasting: " + intent);
- mAm.broadcastIntent(null, intent, null, receiver, 0, null, null, null, true, false);
+ mAm.broadcastIntent(null, intent, null, receiver, 0, null, null, null, true, false,
+ Binder.getOrigCallingUser());
receiver.waitForFinish();
}
@@ -722,6 +726,14 @@
mAm.setDebugApp(null, false, true);
}
+ private void runSwitchUser() throws Exception {
+ if (android.os.Process.myUid() != 0) {
+ throw new RuntimeException("switchuser can only be run as root");
+ }
+ String user = nextArgRequired();
+ mAm.switchUser(Integer.parseInt(user));
+ }
+
class MyActivityController extends IActivityController.Stub {
final String mGdbPort;
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index dd92bbe..203d180 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -148,6 +148,48 @@
return delete_dir_contents(pkgdir, 1, NULL);
}
+int clone_persona_data(uid_t src_persona, uid_t target_persona, int copy)
+{
+ char src_data_dir[PKG_PATH_MAX];
+ char pkg_path[PKG_PATH_MAX];
+ DIR *d;
+ struct dirent *de;
+ struct stat s;
+ uid_t uid;
+
+ if (create_persona_path(src_data_dir, src_persona)) {
+ return -1;
+ }
+
+ d = opendir(src_data_dir);
+ if (d != NULL) {
+ while ((de = readdir(d))) {
+ const char *name = de->d_name;
+
+ if (de->d_type == DT_DIR) {
+ int subfd;
+ /* always skip "." and ".." */
+ if (name[0] == '.') {
+ if (name[1] == 0) continue;
+ if ((name[1] == '.') && (name[2] == 0)) continue;
+ }
+ /* Create the full path to the package's data dir */
+ create_pkg_path(pkg_path, name, PKG_DIR_POSTFIX, src_persona);
+ /* Get the file stat */
+ if (stat(pkg_path, &s) < 0) continue;
+ /* Get the uid of the package */
+ ALOGI("Adding datadir for uid = %d\n", s.st_uid);
+ uid = (uid_t) s.st_uid % PER_USER_RANGE;
+ /* Create the directory for the target */
+ make_user_data(name, uid + target_persona * PER_USER_RANGE,
+ target_persona);
+ }
+ }
+ closedir(d);
+ }
+ return 0;
+}
+
int delete_cache(const char *pkgname)
{
char cachedir[PKG_PATH_MAX];
diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c
index 569b491..7f94a96 100644
--- a/cmds/installd/installd.c
+++ b/cmds/installd/installd.c
@@ -107,6 +107,11 @@
return delete_persona(atoi(arg[0])); /* userid */
}
+static int do_clone_user_data(char **arg, char reply[REPLY_MAX])
+{
+ return clone_persona_data(atoi(arg[0]), atoi(arg[1]), atoi(arg[2]));
+}
+
static int do_movefiles(char **arg, char reply[REPLY_MAX])
{
return movefiles();
@@ -146,6 +151,7 @@
{ "unlinklib", 1, do_unlinklib },
{ "mkuserdata", 3, do_mk_user_data },
{ "rmuser", 1, do_rm_user },
+ { "cloneuserdata", 3, do_clone_user_data },
};
static int readx(int s, void *_buf, int count)
diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h
index 173cabf..78342bb 100644
--- a/cmds/installd/installd.h
+++ b/cmds/installd/installd.h
@@ -72,6 +72,9 @@
#define PKG_NAME_MAX 128 /* largest allowed package name */
#define PKG_PATH_MAX 256 /* max size of any path we use */
+#define PER_USER_RANGE ((uid_t)100000) /* range of uids per user
+ uid = persona * PER_USER_RANGE + appid */
+
/* data structures */
typedef struct {
@@ -143,6 +146,7 @@
int delete_user_data(const char *pkgname, uid_t persona);
int make_user_data(const char *pkgname, uid_t uid, uid_t persona);
int delete_persona(uid_t persona);
+int clone_persona_data(uid_t src_persona, uid_t target_persona, int copy);
int delete_cache(const char *pkgname);
int move_dex(const char *src, const char *dst);
int rm_dex(const char *path);
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index c0ba543..f457842 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -16,8 +16,6 @@
package com.android.commands.pm;
-import com.android.internal.content.PackageHelper;
-
import android.app.ActivityManagerNative;
import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
@@ -33,14 +31,17 @@
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
+import android.content.pm.UserInfo;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.net.Uri;
-import android.os.Parcel;
+import android.os.Binder;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
+import com.android.internal.content.PackageHelper;
+
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
@@ -135,13 +136,18 @@
return;
}
- if ("createUser".equals(op)) {
- runCreateUser();
+ if ("create-profile".equals(op)) {
+ runCreateProfile();
return;
}
- if ("removeUser".equals(op)) {
- runRemoveUser();
+ if ("remove-profile".equals(op)) {
+ runRemoveProfile();
+ return;
+ }
+
+ if ("list-profiles".equals(op)) {
+ runListProfiles();
return;
}
@@ -829,10 +835,10 @@
}
}
- public void runCreateUser() {
+ public void runCreateProfile() {
// Need to be run as root
if (Process.myUid() != ROOT_UID) {
- System.err.println("Error: createUser must be run as root");
+ System.err.println("Error: create-profile must be run as root");
return;
}
String name;
@@ -845,7 +851,7 @@
name = arg;
try {
if (mPm.createUser(name, 0) == null) {
- System.err.println("Error: couldn't create user.");
+ System.err.println("Error: couldn't create profile.");
showUsage();
}
} catch (RemoteException e) {
@@ -855,10 +861,10 @@
}
- public void runRemoveUser() {
+ public void runRemoveProfile() {
// Need to be run as root
if (Process.myUid() != ROOT_UID) {
- System.err.println("Error: removeUser must be run as root");
+ System.err.println("Error: remove-profile must be run as root");
return;
}
int userId;
@@ -877,7 +883,7 @@
}
try {
if (!mPm.removeUser(userId)) {
- System.err.println("Error: couldn't remove user.");
+ System.err.println("Error: couldn't remove profile.");
showUsage();
}
} catch (RemoteException e) {
@@ -886,6 +892,27 @@
}
}
+ public void runListProfiles() {
+ // Need to be run as root
+ if (Process.myUid() != ROOT_UID) {
+ System.err.println("Error: list-profiles must be run as root");
+ return;
+ }
+ try {
+ List<UserInfo> users = mPm.getUsers();
+ if (users == null) {
+ System.err.println("Error: couldn't get users");
+ } else {
+ System.out.println("Users:");
+ for (int i = 0; i < users.size(); i++) {
+ System.out.println("\t" + users.get(i).toString());
+ }
+ }
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(PM_NOT_RUNNING_ERR);
+ }
+ }
class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
boolean finished;
boolean result;
@@ -966,7 +993,8 @@
ClearDataObserver obs = new ClearDataObserver();
try {
- if (!ActivityManagerNative.getDefault().clearApplicationUserData(pkg, obs)) {
+ if (!ActivityManagerNative.getDefault().clearApplicationUserData(pkg, obs,
+ Binder.getOrigCallingUser())) {
System.err.println("Failed");
}
@@ -1132,8 +1160,8 @@
System.err.println(" pm disable-user PACKAGE_OR_COMPONENT");
System.err.println(" pm set-install-location [0/auto] [1/internal] [2/external]");
System.err.println(" pm get-install-location");
- System.err.println(" pm createUser USER_NAME");
- System.err.println(" pm removeUser USER_ID");
+ System.err.println(" pm create-profile USER_NAME");
+ System.err.println(" pm remove-profile USER_ID");
System.err.println("");
System.err.println("pm list packages: prints all packages, optionally only");
System.err.println(" those whose package name contains the text in FILTER. Options:");
diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java
index 274a9d5..634e4d8 100644
--- a/core/java/android/animation/LayoutTransition.java
+++ b/core/java/android/animation/LayoutTransition.java
@@ -960,17 +960,17 @@
if (anim instanceof ObjectAnimator) {
((ObjectAnimator) anim).setCurrentPlayTime(0);
}
- if (mListeners != null) {
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator anim) {
- currentAppearingAnimations.remove(child);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator anim) {
+ currentAppearingAnimations.remove(child);
+ if (mListeners != null) {
for (TransitionListener listener : mListeners) {
listener.endTransition(LayoutTransition.this, parent, child, APPEARING);
}
}
- });
- }
+ }
+ });
currentAppearingAnimations.put(child, anim);
anim.start();
}
@@ -998,17 +998,19 @@
anim.setStartDelay(mDisappearingDelay);
anim.setDuration(mDisappearingDuration);
anim.setTarget(child);
- if (mListeners != null) {
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator anim) {
- currentDisappearingAnimations.remove(child);
+ final float preAnimAlpha = child.getAlpha();
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator anim) {
+ currentDisappearingAnimations.remove(child);
+ child.setAlpha(preAnimAlpha);
+ if (mListeners != null) {
for (TransitionListener listener : mListeners) {
listener.endTransition(LayoutTransition.this, parent, child, DISAPPEARING);
}
}
- });
- }
+ }
+ });
if (anim instanceof ObjectAnimator) {
((ObjectAnimator) anim).setCurrentPlayTime(0);
}
diff --git a/core/java/android/animation/TimeAnimator.java b/core/java/android/animation/TimeAnimator.java
index 0a96d59..a79f2a3 100644
--- a/core/java/android/animation/TimeAnimator.java
+++ b/core/java/android/animation/TimeAnimator.java
@@ -1,13 +1,11 @@
package android.animation;
/**
- * This class provides a simple callback mechanism to listeners that is synchronized with other
- * animators in the system. There is no duration, interpolation, or object value-setting
- * with this Animator. Instead, it is simply started and proceeds to send out events on every
- * animation frame to its TimeListener (if set), with information about this animator,
- * the total elapsed time, and the time since the last animation frame.
- *
- * @hide
+ * This class provides a simple callback mechanism to listeners that is synchronized with all
+ * other animators in the system. There is no duration, interpolation, or object value-setting
+ * with this Animator. Instead, it is simply started, after which it proceeds to send out events
+ * on every animation frame to its TimeListener (if set), with information about this animator,
+ * the total elapsed time, and the elapsed time since the previous animation frame.
*/
public class TimeAnimator extends ValueAnimator {
@@ -59,10 +57,10 @@
* Implementors of this interface can set themselves as update listeners
* to a <code>TimeAnimator</code> instance to receive callbacks on every animation
* frame to receive the total time since the animator started and the delta time
- * since the last frame. The first time the listener is called, totalTime and
- * deltaTime should both be zero.
- *
- * @hide
+ * since the last frame. The first time the listener is called,
+ * deltaTime will be zero. The same is true for totalTime, unless the animator was
+ * set to a specific {@link ValueAnimator#setCurrentPlayTime(long) currentPlayTime}
+ * prior to starting.
*/
public static interface TimeListener {
/**
@@ -70,7 +68,8 @@
* along with information about the elapsed time.</p>
*
* @param animation The animator sending out the notification.
- * @param totalTime The
+ * @param totalTime The total time elapsed since the animator started, in milliseconds.
+ * @param deltaTime The time elapsed since the previous frame, in milliseconds.
*/
void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime);
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 3f28e67..cc1efb9 100755
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -54,7 +54,6 @@
* Internal constants
*/
private static float sDurationScale = 1.0f;
- private static boolean sDurationScaleInitialized = false;
/**
* Messages sent to timing handler: START is sent when an animation first begins.
@@ -161,7 +160,7 @@
//
// How long the animation should last in ms
- private long mDuration = 300;
+ private long mDuration = (long)(300 * sDurationScale);
private long mUnscaledDuration = 300;
// The amount of time in ms to delay starting the animation after start() is called
@@ -222,21 +221,20 @@
*/
public static final int INFINITE = -1;
+
+ /**
+ * @hide
+ */
+ public static void setDurationScale(float durationScale) {
+ sDurationScale = durationScale;
+ }
+
/**
* Creates a new ValueAnimator object. This default constructor is primarily for
* use internally; the factory methods which take parameters are more generally
* useful.
*/
public ValueAnimator() {
- if (!sDurationScaleInitialized) {
- // Scale value initialized per-process when first animator is constructed
- String scaleString = SystemProperties.get("persist.sys.ui.animation");
- if (!scaleString.isEmpty()) {
- sDurationScale = Float.parseFloat(scaleString);
- }
- sDurationScaleInitialized = true;
- }
- mDuration *= sDurationScale;
}
/**
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 9661b9e..d98d87b 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -30,6 +30,7 @@
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Point;
+import android.os.Binder;
import android.os.Debug;
import android.os.Handler;
import android.os.Parcel;
@@ -975,7 +976,7 @@
public boolean clearApplicationUserData(String packageName, IPackageDataObserver observer) {
try {
return ActivityManagerNative.getDefault().clearApplicationUserData(packageName,
- observer);
+ observer, Binder.getOrigCallingUser());
} catch (RemoteException e) {
return false;
}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 7994d7c..d80902d 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -91,7 +91,7 @@
try {
getDefault().broadcastIntent(
null, intent, null, null, Activity.RESULT_OK, null, null,
- null /*permission*/, false, true);
+ null /*permission*/, false, true, Binder.getOrigCallingUser());
} catch (RemoteException ex) {
}
}
@@ -306,9 +306,10 @@
String perm = data.readString();
boolean serialized = data.readInt() != 0;
boolean sticky = data.readInt() != 0;
+ int userId = data.readInt();
int res = broadcastIntent(app, intent, resolvedType, resultTo,
resultCode, resultData, resultExtras, perm,
- serialized, sticky);
+ serialized, sticky, userId);
reply.writeNoException();
reply.writeInt(res);
return true;
@@ -320,7 +321,8 @@
IBinder b = data.readStrongBinder();
IApplicationThread app = b != null ? ApplicationThreadNative.asInterface(b) : null;
Intent intent = Intent.CREATOR.createFromParcel(data);
- unbroadcastIntent(app, intent);
+ int userId = data.readInt();
+ unbroadcastIntent(app, intent, userId);
reply.writeNoException();
return true;
}
@@ -900,7 +902,8 @@
String packageName = data.readString();
IPackageDataObserver observer = IPackageDataObserver.Stub.asInterface(
data.readStrongBinder());
- boolean res = clearApplicationUserData(packageName, observer);
+ int userId = data.readInt();
+ boolean res = clearApplicationUserData(packageName, observer, userId);
reply.writeNoException();
reply.writeInt(res ? 1 : 0);
return true;
@@ -1819,7 +1822,7 @@
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle map,
String requiredPermission, boolean serialized,
- boolean sticky) throws RemoteException
+ boolean sticky, int userId) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -1834,6 +1837,7 @@
data.writeString(requiredPermission);
data.writeInt(serialized ? 1 : 0);
data.writeInt(sticky ? 1 : 0);
+ data.writeInt(userId);
mRemote.transact(BROADCAST_INTENT_TRANSACTION, data, reply, 0);
reply.readException();
int res = reply.readInt();
@@ -1841,13 +1845,15 @@
data.recycle();
return res;
}
- public void unbroadcastIntent(IApplicationThread caller, Intent intent) throws RemoteException
+ public void unbroadcastIntent(IApplicationThread caller, Intent intent, 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.writeInt(userId);
mRemote.transact(UNBROADCAST_INTENT_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
@@ -2651,12 +2657,13 @@
return res;
}
public boolean clearApplicationUserData(final String packageName,
- final IPackageDataObserver observer) throws RemoteException {
+ final IPackageDataObserver observer, final int userId) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeString(packageName);
data.writeStrongBinder(observer.asBinder());
+ data.writeInt(userId);
mRemote.transact(CLEAR_APP_DATA_TRANSACTION, data, reply, 0);
reply.readException();
boolean res = reply.readInt() != 0;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3c5f53a..e4cfc99 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -47,6 +47,7 @@
import android.net.ProxyProperties;
import android.opengl.GLUtils;
import android.os.AsyncTask;
+import android.os.Binder;
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
@@ -60,6 +61,7 @@
import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.SystemClock;
+import android.os.UserId;
import android.util.AndroidRuntimeException;
import android.util.DisplayMetrics;
import android.util.EventLog;
@@ -132,6 +134,7 @@
private static final boolean DEBUG_RESULTS = false;
private static final boolean DEBUG_BACKUP = true;
private static final boolean DEBUG_CONFIGURATION = false;
+ private static final boolean DEBUG_SERVICE = true;
private static final long MIN_TIME_BETWEEN_GCS = 5*1000;
private static final Pattern PATTERN_SEMICOLON = Pattern.compile(";");
private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003;
@@ -635,6 +638,9 @@
s.intent = intent;
s.rebind = rebind;
+ if (DEBUG_SERVICE)
+ Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid="
+ + Binder.getCallingUid() + " pid=" + Binder.getCallingPid());
queueOrSendMessage(H.BIND_SERVICE, s);
}
@@ -1592,7 +1598,8 @@
boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0;
boolean securityViolation = includeCode && ai.uid != 0
&& ai.uid != Process.SYSTEM_UID && (mBoundApplication != null
- ? ai.uid != mBoundApplication.appInfo.uid : true);
+ ? !UserId.isSameApp(ai.uid, mBoundApplication.appInfo.uid)
+ : true);
if ((flags&(Context.CONTEXT_INCLUDE_CODE
|Context.CONTEXT_IGNORE_SECURITY))
== Context.CONTEXT_INCLUDE_CODE) {
@@ -2294,6 +2301,8 @@
private void handleBindService(BindServiceData data) {
Service s = mServices.get(data.token);
+ if (DEBUG_SERVICE)
+ Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind);
if (s != null) {
try {
data.intent.setExtrasClassLoader(s.getClassLoader());
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 180a442..fee2beb 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1177,13 +1177,14 @@
*/
@Override
public List<UserInfo> getUsers() {
- // TODO:
- // Dummy code, always returns just the primary user
- ArrayList<UserInfo> users = new ArrayList<UserInfo>();
- UserInfo primary = new UserInfo(0, "Root!",
- UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
- users.add(primary);
- return users;
+ try {
+ return mPM.getUsers();
+ } catch (RemoteException re) {
+ ArrayList<UserInfo> users = new ArrayList<UserInfo>();
+ UserInfo primary = new UserInfo(0, "Root!", UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
+ users.add(primary);
+ return users;
+ }
}
/**
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 2bf1fb7..db5113e 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -75,6 +75,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserId;
import android.os.Vibrator;
import android.os.storage.StorageManager;
import android.telephony.TelephonyManager;
@@ -896,7 +897,8 @@
intent.setAllowFds(false);
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
- Activity.RESULT_OK, null, null, null, false, false);
+ Activity.RESULT_OK, null, null, null, false, false,
+ Binder.getOrigCallingUser());
} catch (RemoteException e) {
}
}
@@ -908,7 +910,8 @@
intent.setAllowFds(false);
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
- Activity.RESULT_OK, null, null, receiverPermission, false, false);
+ Activity.RESULT_OK, null, null, receiverPermission, false, false,
+ Binder.getOrigCallingUser());
} catch (RemoteException e) {
}
}
@@ -921,7 +924,8 @@
intent.setAllowFds(false);
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
- Activity.RESULT_OK, null, null, receiverPermission, true, false);
+ Activity.RESULT_OK, null, null, receiverPermission, true, false,
+ Binder.getOrigCallingUser());
} catch (RemoteException e) {
}
}
@@ -954,7 +958,7 @@
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, rd,
initialCode, initialData, initialExtras, receiverPermission,
- true, false);
+ true, false, Binder.getOrigCallingUser());
} catch (RemoteException e) {
}
}
@@ -966,7 +970,8 @@
intent.setAllowFds(false);
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
- Activity.RESULT_OK, null, null, null, false, true);
+ Activity.RESULT_OK, null, null, null, false, true,
+ Binder.getOrigCallingUser());
} catch (RemoteException e) {
}
}
@@ -999,7 +1004,7 @@
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, rd,
initialCode, initialData, initialExtras, null,
- true, true);
+ true, true, Binder.getOrigCallingUser());
} catch (RemoteException e) {
}
}
@@ -1014,7 +1019,7 @@
try {
intent.setAllowFds(false);
ActivityManagerNative.getDefault().unbroadcastIntent(
- mMainThread.getApplicationThread(), intent);
+ mMainThread.getApplicationThread(), intent, Binder.getOrigCallingUser());
} catch (RemoteException e) {
}
}
@@ -1215,8 +1220,7 @@
int pid = Binder.getCallingPid();
if (pid != Process.myPid()) {
- return checkPermission(permission, pid,
- Binder.getCallingUid());
+ return checkPermission(permission, pid, Binder.getCallingUid());
}
return PackageManager.PERMISSION_DENIED;
}
@@ -1384,7 +1388,8 @@
Uri uri, int modeFlags, String message) {
enforceForUri(
modeFlags, checkCallingUriPermission(uri, modeFlags),
- false, Binder.getCallingUid(), uri, message);
+ false,
+ Binder.getCallingUid(), uri, message);
}
public void enforceCallingOrSelfUriPermission(
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 5222d37..39817ac 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -114,8 +114,8 @@
public int broadcastIntent(IApplicationThread caller, Intent intent,
String resolvedType, IIntentReceiver resultTo, int resultCode,
String resultData, Bundle map, String requiredPermission,
- boolean serialized, boolean sticky) throws RemoteException;
- public void unbroadcastIntent(IApplicationThread caller, Intent intent) throws RemoteException;
+ boolean serialized, boolean sticky, int userId) throws RemoteException;
+ public void unbroadcastIntent(IApplicationThread caller, Intent intent, int userId) throws RemoteException;
/* oneway */
public void finishReceiver(IBinder who, int resultCode, String resultData, Bundle map, boolean abortBroadcast) throws RemoteException;
public void attachApplication(IApplicationThread app) throws RemoteException;
@@ -209,7 +209,7 @@
int flags) throws RemoteException;
public void cancelIntentSender(IIntentSender sender) throws RemoteException;
public boolean clearApplicationUserData(final String packageName,
- final IPackageDataObserver observer) throws RemoteException;
+ final IPackageDataObserver observer, int userId) throws RemoteException;
public String getPackageForIntentSender(IIntentSender sender) throws RemoteException;
public void setProcessLimit(int max) throws RemoteException;
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 0c6baeb..fcbcd81 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -36,6 +36,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.StrictMode;
+import android.os.UserId;
import android.util.AndroidRuntimeException;
import android.util.Slog;
import android.view.CompatibilityInfoHolder;
@@ -67,6 +68,8 @@
*/
public final class LoadedApk {
+ private static final String TAG = "LoadedApk";
+
private final ActivityThread mActivityThread;
private final ApplicationInfo mApplicationInfo;
final String mPackageName;
@@ -113,8 +116,13 @@
mApplicationInfo = aInfo;
mPackageName = aInfo.packageName;
mAppDir = aInfo.sourceDir;
- mResDir = aInfo.uid == Process.myUid() ? aInfo.sourceDir
+ final int myUid = Process.myUid();
+ mResDir = aInfo.uid == myUid ? aInfo.sourceDir
: aInfo.publicSourceDir;
+ if (!UserId.isSameUser(aInfo.uid, myUid)) {
+ aInfo.dataDir = PackageManager.getDataDirForUser(UserId.getUserId(myUid),
+ mPackageName);
+ }
mSharedLibraries = aInfo.sharedLibraryFiles;
mDataDir = aInfo.dataDir;
mDataDirFile = mDataDir != null ? new File(mDataDir) : null;
diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java
index d83d2e6..ff71ee7 100644
--- a/core/java/android/app/LoaderManager.java
+++ b/core/java/android/app/LoaderManager.java
@@ -17,6 +17,7 @@
package android.app;
import android.content.Loader;
+import android.content.Loader.OnLoadCanceledListener;
import android.os.Bundle;
import android.util.DebugUtils;
import android.util.Log;
@@ -219,7 +220,8 @@
boolean mCreatingLoader;
- final class LoaderInfo implements Loader.OnLoadCompleteListener<Object> {
+ final class LoaderInfo implements Loader.OnLoadCompleteListener<Object>,
+ Loader.OnLoadCanceledListener<Object> {
final int mId;
final Bundle mArgs;
LoaderManager.LoaderCallbacks<Object> mCallbacks;
@@ -271,6 +273,7 @@
}
if (!mListenerRegistered) {
mLoader.registerListener(mId, this);
+ mLoader.registerOnLoadCanceledListener(this);
mListenerRegistered = true;
}
mLoader.startLoading();
@@ -329,11 +332,21 @@
// Let the loader know we're done with it
mListenerRegistered = false;
mLoader.unregisterListener(this);
+ mLoader.unregisterOnLoadCanceledListener(this);
mLoader.stopLoading();
}
}
}
-
+
+ void cancel() {
+ if (DEBUG) Log.v(TAG, " Canceling: " + this);
+ if (mStarted && mLoader != null && mListenerRegistered) {
+ if (!mLoader.cancelLoad()) {
+ onLoadCanceled(mLoader);
+ }
+ }
+ }
+
void destroy() {
if (DEBUG) Log.v(TAG, " Destroying: " + this);
mDestroyed = true;
@@ -361,6 +374,7 @@
if (mListenerRegistered) {
mListenerRegistered = false;
mLoader.unregisterListener(this);
+ mLoader.unregisterOnLoadCanceledListener(this);
}
mLoader.reset();
}
@@ -368,8 +382,38 @@
mPendingLoader.destroy();
}
}
-
- @Override public void onLoadComplete(Loader<Object> loader, Object data) {
+
+ @Override
+ public void onLoadCanceled(Loader<Object> loader) {
+ if (DEBUG) Log.v(TAG, "onLoadCanceled: " + this);
+
+ if (mDestroyed) {
+ if (DEBUG) Log.v(TAG, " Ignoring load canceled -- destroyed");
+ return;
+ }
+
+ if (mLoaders.get(mId) != this) {
+ // This cancellation message is not coming from the current active loader.
+ // We don't care about it.
+ if (DEBUG) Log.v(TAG, " Ignoring load canceled -- not active");
+ return;
+ }
+
+ LoaderInfo pending = mPendingLoader;
+ if (pending != null) {
+ // There is a new request pending and we were just
+ // waiting for the old one to cancel or complete before starting
+ // it. So now it is time, switch over to the new loader.
+ if (DEBUG) Log.v(TAG, " Switching to pending loader: " + pending);
+ mPendingLoader = null;
+ mLoaders.put(mId, null);
+ destroy();
+ installLoader(pending);
+ }
+ }
+
+ @Override
+ public void onLoadComplete(Loader<Object> loader, Object data) {
if (DEBUG) Log.v(TAG, "onLoadComplete: " + this);
if (mDestroyed) {
@@ -632,7 +676,9 @@
} else {
// Now we have three active loaders... we'll queue
// up this request to be processed once one of the other loaders
- // finishes.
+ // finishes or is canceled.
+ if (DEBUG) Log.v(TAG, " Current loader is running; attempting to cancel");
+ info.cancel();
if (info.mPendingLoader != null) {
if (DEBUG) Log.v(TAG, " Removing pending loader: " + info.mPendingLoader);
info.mPendingLoader.destroy();
diff --git a/core/java/android/content/AsyncTaskLoader.java b/core/java/android/content/AsyncTaskLoader.java
index 944ca6b..da51952 100644
--- a/core/java/android/content/AsyncTaskLoader.java
+++ b/core/java/android/content/AsyncTaskLoader.java
@@ -53,19 +53,33 @@
static final boolean DEBUG = false;
final class LoadTask extends AsyncTask<Void, Void, D> implements Runnable {
+ private final CountDownLatch mDone = new CountDownLatch(1);
- D result;
+ // Set to true to indicate that the task has been posted to a handler for
+ // execution at a later time. Used to throttle updates.
boolean waiting;
- private CountDownLatch done = new CountDownLatch(1);
-
/* Runs on a worker thread */
@Override
protected D doInBackground(Void... params) {
if (DEBUG) Slog.v(TAG, this + " >>> doInBackground");
- result = AsyncTaskLoader.this.onLoadInBackground();
- if (DEBUG) Slog.v(TAG, this + " <<< doInBackground");
- return result;
+ try {
+ D data = AsyncTaskLoader.this.onLoadInBackground();
+ if (DEBUG) Slog.v(TAG, this + " <<< doInBackground");
+ return data;
+ } catch (OperationCanceledException ex) {
+ if (!isCancelled()) {
+ // onLoadInBackground threw a canceled exception spuriously.
+ // This is problematic because it means that the LoaderManager did not
+ // cancel the Loader itself and still expects to receive a result.
+ // Additionally, the Loader's own state will not have been updated to
+ // reflect the fact that the task was being canceled.
+ // So we treat this case as an unhandled exception.
+ throw ex;
+ }
+ if (DEBUG) Slog.v(TAG, this + " <<< doInBackground (was canceled)");
+ return null;
+ }
}
/* Runs on the UI thread */
@@ -75,25 +89,37 @@
try {
AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);
} finally {
- done.countDown();
+ mDone.countDown();
}
}
+ /* Runs on the UI thread */
@Override
- protected void onCancelled() {
+ protected void onCancelled(D data) {
if (DEBUG) Slog.v(TAG, this + " onCancelled");
try {
- AsyncTaskLoader.this.dispatchOnCancelled(this, result);
+ AsyncTaskLoader.this.dispatchOnCancelled(this, data);
} finally {
- done.countDown();
+ mDone.countDown();
}
}
+ /* Runs on the UI thread, when the waiting task is posted to a handler.
+ * This method is only executed when task execution was deferred (waiting was true). */
@Override
public void run() {
waiting = false;
AsyncTaskLoader.this.executePendingTask();
}
+
+ /* Used for testing purposes to wait for the task to complete. */
+ public void waitForLoader() {
+ try {
+ mDone.await();
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
}
volatile LoadTask mTask;
@@ -109,7 +135,7 @@
/**
* Set amount to throttle updates by. This is the minimum time from
- * when the last {@link #onLoadInBackground()} call has completed until
+ * when the last {@link #loadInBackground()} call has completed until
* a new load is scheduled.
*
* @param delayMS Amount of delay, in milliseconds.
@@ -130,24 +156,9 @@
executePendingTask();
}
- /**
- * Attempt to cancel the current load task. See {@link AsyncTask#cancel(boolean)}
- * for more info. Must be called on the main thread of the process.
- *
- * <p>Cancelling is not an immediate operation, since the load is performed
- * in a background thread. If there is currently a load in progress, this
- * method requests that the load be cancelled, and notes this is the case;
- * once the background thread has completed its work its remaining state
- * will be cleared. If another load request comes in during this time,
- * it will be held until the cancelled load is complete.
- *
- * @return Returns <tt>false</tt> if the task could not be cancelled,
- * typically because it has already completed normally, or
- * because {@link #startLoading()} hasn't been called; returns
- * <tt>true</tt> otherwise.
- */
- public boolean cancelLoad() {
- if (DEBUG) Slog.v(TAG, "cancelLoad: mTask=" + mTask);
+ @Override
+ protected boolean onCancelLoad() {
+ if (DEBUG) Slog.v(TAG, "onCancelLoad: mTask=" + mTask);
if (mTask != null) {
if (mCancellingTask != null) {
// There was a pending task already waiting for a previous
@@ -173,7 +184,7 @@
if (DEBUG) Slog.v(TAG, "cancelLoad: cancelled=" + cancelled);
if (cancelled) {
mCancellingTask = mTask;
- onCancelLoadInBackground();
+ cancelLoadInBackground();
}
mTask = null;
return cancelled;
@@ -184,7 +195,10 @@
/**
* Called if the task was canceled before it was completed. Gives the class a chance
- * to properly dispose of the result.
+ * to clean up post-cancellation and to properly dispose of the result.
+ *
+ * @param data The value that was returned by {@link #loadInBackground}, or null
+ * if the task threw {@link OperationCanceledException}.
*/
public void onCanceled(D data) {
}
@@ -218,6 +232,8 @@
if (DEBUG) Slog.v(TAG, "Cancelled task is now canceled!");
mLastLoadCompleteTime = SystemClock.uptimeMillis();
mCancellingTask = null;
+ if (DEBUG) Slog.v(TAG, "Delivering cancellation");
+ deliverCancellation();
executePendingTask();
}
}
@@ -240,38 +256,72 @@
}
/**
+ * Called on a worker thread to perform the actual load and to return
+ * the result of the load operation.
+ *
+ * Implementations should not deliver the result directly, but should return them
+ * from this method, which will eventually end up calling {@link #deliverResult} on
+ * the UI thread. If implementations need to process the results on the UI thread
+ * they may override {@link #deliverResult} and do so there.
+ *
+ * To support cancellation, this method should periodically check the value of
+ * {@link #isLoadInBackgroundCanceled} and terminate when it returns true.
+ * Subclasses may also override {@link #cancelLoadInBackground} to interrupt the load
+ * directly instead of polling {@link #isLoadInBackgroundCanceled}.
+ *
+ * When the load is canceled, this method may either return normally or throw
+ * {@link OperationCanceledException}. In either case, the {@link Loader} will
+ * call {@link #onCanceled} to perform post-cancellation cleanup and to dispose of the
+ * result object, if any.
+ *
+ * @return The result of the load operation.
+ *
+ * @throws OperationCanceledException if the load is canceled during execution.
+ *
+ * @see #isLoadInBackgroundCanceled
+ * @see #cancelLoadInBackground
+ * @see #onCanceled
*/
public abstract D loadInBackground();
/**
- * Called on a worker thread to perform the actual load. Implementations should not deliver the
- * result directly, but should return them from this method, which will eventually end up
- * calling {@link #deliverResult} on the UI thread. If implementations need to process
- * the results on the UI thread they may override {@link #deliverResult} and do so
- * there.
+ * Calls {@link #loadInBackground()}.
*
- * @return Implementations must return the result of their load operation.
+ * This method is reserved for use by the loader framework.
+ * Subclasses should override {@link #loadInBackground} instead of this method.
+ *
+ * @return The result of the load operation.
+ *
+ * @throws OperationCanceledException if the load is canceled during execution.
+ *
+ * @see #loadInBackground
*/
protected D onLoadInBackground() {
return loadInBackground();
}
/**
- * Override this method to try to abort the computation currently taking
- * place on a background thread.
+ * Called on the main thread to abort a load in progress.
*
- * Note that when this method is called, it is possible that {@link #loadInBackground}
- * has not started yet or has already completed.
+ * Override this method to abort the current invocation of {@link #loadInBackground}
+ * that is running in the background on a worker thread.
+ *
+ * This method should do nothing if {@link #loadInBackground} has not started
+ * running or if it has already finished.
+ *
+ * @see #loadInBackground
*/
- protected void onCancelLoadInBackground() {
+ public void cancelLoadInBackground() {
}
/**
- * Returns true if the current execution of {@link #loadInBackground()} is being canceled.
+ * Returns true if the current invocation of {@link #loadInBackground} is being canceled.
*
- * @return True if the current execution of {@link #loadInBackground()} is being canceled.
+ * @return True if the current invocation of {@link #loadInBackground} is being canceled.
+ *
+ * @see #loadInBackground
*/
- protected boolean isLoadInBackgroundCanceled() {
+ public boolean isLoadInBackgroundCanceled() {
return mCancellingTask != null;
}
@@ -288,11 +338,7 @@
public void waitForLoader() {
LoadTask task = mTask;
if (task != null) {
- try {
- task.done.await();
- } catch (InterruptedException e) {
- // Ignore
- }
+ task.waitForLoader();
}
}
diff --git a/core/java/android/content/CancelationSignal.java b/core/java/android/content/CancellationSignal.java
similarity index 69%
rename from core/java/android/content/CancelationSignal.java
rename to core/java/android/content/CancellationSignal.java
index 58cf59d..2dbbe54 100644
--- a/core/java/android/content/CancelationSignal.java
+++ b/core/java/android/content/CancellationSignal.java
@@ -21,15 +21,15 @@
/**
* Provides the ability to cancel an operation in progress.
*/
-public final class CancelationSignal {
+public final class CancellationSignal {
private boolean mIsCanceled;
private OnCancelListener mOnCancelListener;
- private ICancelationSignal mRemote;
+ private ICancellationSignal mRemote;
/**
- * Creates a cancelation signal, initially not canceled.
+ * Creates a cancellation signal, initially not canceled.
*/
- public CancelationSignal() {
+ public CancellationSignal() {
}
/**
@@ -55,7 +55,7 @@
}
/**
- * Cancels the operation and signals the cancelation listener.
+ * Cancels the operation and signals the cancellation listener.
* If the operation has not yet started, then it will be canceled as soon as it does.
*/
public void cancel() {
@@ -76,17 +76,23 @@
}
/**
- * Sets the cancelation listener to be called when canceled.
- * If {@link CancelationSignal#cancel} has already been called, then the provided
+ * Sets the cancellation listener to be called when canceled.
+ *
+ * This method is intended to be used by the recipient of a cancellation signal
+ * such as a database or a content provider to handle cancellation requests
+ * while performing a long-running operation. This method is not intended to be
+ * used by applications themselves.
+ *
+ * If {@link CancellationSignal#cancel} has already been called, then the provided
* listener is invoked immediately.
*
- * The listener is called while holding the cancelation signal's lock which is
+ * The listener is called while holding the cancellation signal's lock which is
* also held while registering or unregistering the listener. Because of the lock,
* it is not possible for the listener to run after it has been unregistered.
- * This design choice makes it easier for clients of {@link CancelationSignal} to
+ * This design choice makes it easier for clients of {@link CancellationSignal} to
* prevent race conditions related to listener registration and unregistration.
*
- * @param listener The cancelation listener, or null to remove the current listener.
+ * @param listener The cancellation listener, or null to remove the current listener.
*/
public void setOnCancelListener(OnCancelListener listener) {
synchronized (this) {
@@ -104,7 +110,7 @@
*
* @hide
*/
- public void setRemote(ICancelationSignal remote) {
+ public void setRemote(ICancellationSignal remote) {
synchronized (this) {
mRemote = remote;
if (mIsCanceled && remote != null) {
@@ -118,47 +124,47 @@
/**
* Creates a transport that can be returned back to the caller of
- * a Binder function and subsequently used to dispatch a cancelation signal.
+ * a Binder function and subsequently used to dispatch a cancellation signal.
*
- * @return The new cancelation signal transport.
+ * @return The new cancellation signal transport.
*
* @hide
*/
- public static ICancelationSignal createTransport() {
+ public static ICancellationSignal createTransport() {
return new Transport();
}
/**
- * Given a locally created transport, returns its associated cancelation signal.
+ * Given a locally created transport, returns its associated cancellation signal.
*
* @param transport The locally created transport, or null if none.
- * @return The associated cancelation signal, or null if none.
+ * @return The associated cancellation signal, or null if none.
*
* @hide
*/
- public static CancelationSignal fromTransport(ICancelationSignal transport) {
+ public static CancellationSignal fromTransport(ICancellationSignal transport) {
if (transport instanceof Transport) {
- return ((Transport)transport).mCancelationSignal;
+ return ((Transport)transport).mCancellationSignal;
}
return null;
}
/**
- * Listens for cancelation.
+ * Listens for cancellation.
*/
public interface OnCancelListener {
/**
- * Called when {@link CancelationSignal#cancel} is invoked.
+ * Called when {@link CancellationSignal#cancel} is invoked.
*/
void onCancel();
}
- private static final class Transport extends ICancelationSignal.Stub {
- final CancelationSignal mCancelationSignal = new CancelationSignal();
+ private static final class Transport extends ICancellationSignal.Stub {
+ final CancellationSignal mCancellationSignal = new CancellationSignal();
@Override
public void cancel() throws RemoteException {
- mCancelationSignal.cancel();
+ mCancellationSignal.cancel();
}
}
}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index adbeb6a..12e3ccf 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -178,10 +178,10 @@
@Override
public Cursor query(Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder,
- ICancelationSignal cancelationSignal) {
+ ICancellationSignal cancellationSignal) {
enforceReadPermission(uri);
return ContentProvider.this.query(uri, projection, selection, selectionArgs, sortOrder,
- CancelationSignal.fromTransport(cancelationSignal));
+ CancellationSignal.fromTransport(cancellationSignal));
}
@Override
@@ -263,8 +263,8 @@
}
@Override
- public ICancelationSignal createCancelationSignal() throws RemoteException {
- return CancelationSignal.createTransport();
+ public ICancellationSignal createCancellationSignal() throws RemoteException {
+ return CancellationSignal.createTransport();
}
private void enforceReadPermission(Uri uri) {
@@ -557,7 +557,7 @@
String selection, String[] selectionArgs, String sortOrder);
/**
- * Implement this to handle query requests from clients with support for cancelation.
+ * Implement this to handle query requests from clients with support for cancellation.
* This method can be called from multiple threads, as described in
* <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
* and Threads</a>.
@@ -597,9 +597,9 @@
return c;</pre>
* <p>
* If you implement this method then you must also implement the version of
- * {@link #query(Uri, String[], String, String[], String)} that does not take a cancelation
- * provider to ensure correct operation on older versions of the Android Framework in
- * which the cancelation signal overload was not available.
+ * {@link #query(Uri, String[], String, String[], String)} that does not take a cancellation
+ * signal to ensure correct operation on older versions of the Android Framework in
+ * which the cancellation signal overload was not available.
*
* @param uri The URI to query. This will be the full URI sent by the client;
* if the client is requesting a specific record, the URI will end in a record number
@@ -614,14 +614,14 @@
* The values will be bound as Strings.
* @param sortOrder How the rows in the cursor should be sorted.
* If null then the provider is free to define the sort order.
- * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
* If the operation is canceled, then {@link OperationCanceledException} will be thrown
* when the query is executed.
* @return a Cursor or null.
*/
public Cursor query(Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder,
- CancelationSignal cancelationSignal) {
+ CancellationSignal cancellationSignal) {
return query(uri, projection, selection, selectionArgs, sortOrder);
}
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index 9a1fa65..3ac5e07 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -52,15 +52,15 @@
/** See {@link ContentProvider#query ContentProvider.query} */
public Cursor query(Uri url, String[] projection, String selection,
- String[] selectionArgs, String sortOrder, CancelationSignal cancelationSignal)
+ String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal)
throws RemoteException {
- ICancelationSignal remoteCancelationSignal = null;
- if (cancelationSignal != null) {
- remoteCancelationSignal = mContentProvider.createCancelationSignal();
- cancelationSignal.setRemote(remoteCancelationSignal);
+ ICancellationSignal remoteCancellationSignal = null;
+ if (cancellationSignal != null) {
+ remoteCancellationSignal = mContentProvider.createCancellationSignal();
+ cancellationSignal.setRemote(remoteCancellationSignal);
}
return mContentProvider.query(url, projection, selection, selectionArgs, sortOrder,
- remoteCancelationSignal);
+ remoteCancellationSignal);
}
/** See {@link ContentProvider#getType ContentProvider.getType} */
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index e0e277a..eb83dbc 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -105,11 +105,11 @@
String sortOrder = data.readString();
IContentObserver observer = IContentObserver.Stub.asInterface(
data.readStrongBinder());
- ICancelationSignal cancelationSignal = ICancelationSignal.Stub.asInterface(
+ ICancellationSignal cancellationSignal = ICancellationSignal.Stub.asInterface(
data.readStrongBinder());
Cursor cursor = query(url, projection, selection, selectionArgs, sortOrder,
- cancelationSignal);
+ cancellationSignal);
if (cursor != null) {
CursorToBulkCursorAdaptor adaptor = new CursorToBulkCursorAdaptor(
cursor, observer, getProviderName());
@@ -300,9 +300,9 @@
{
data.enforceInterface(IContentProvider.descriptor);
- ICancelationSignal cancelationSignal = createCancelationSignal();
+ ICancellationSignal cancellationSignal = createCancellationSignal();
reply.writeNoException();
- reply.writeStrongBinder(cancelationSignal.asBinder());
+ reply.writeStrongBinder(cancellationSignal.asBinder());
return true;
}
}
@@ -334,7 +334,7 @@
}
public Cursor query(Uri url, String[] projection, String selection,
- String[] selectionArgs, String sortOrder, ICancelationSignal cancelationSignal)
+ String[] selectionArgs, String sortOrder, ICancellationSignal cancellationSignal)
throws RemoteException {
BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
Parcel data = Parcel.obtain();
@@ -363,7 +363,7 @@
}
data.writeString(sortOrder);
data.writeStrongBinder(adaptor.getObserver().asBinder());
- data.writeStrongBinder(cancelationSignal != null ? cancelationSignal.asBinder() : null);
+ data.writeStrongBinder(cancellationSignal != null ? cancellationSignal.asBinder() : null);
mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);
@@ -632,7 +632,7 @@
}
}
- public ICancelationSignal createCancelationSignal() throws RemoteException {
+ public ICancellationSignal createCancellationSignal() throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
@@ -642,9 +642,9 @@
data, reply, 0);
DatabaseUtils.readExceptionFromParcel(reply);
- ICancelationSignal cancelationSignal = ICancelationSignal.Stub.asInterface(
+ ICancellationSignal cancellationSignal = ICancellationSignal.Stub.asInterface(
reply.readStrongBinder());
- return cancelationSignal;
+ return cancellationSignal;
} finally {
data.recycle();
reply.recycle();
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index e79475a..96a65da 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -335,7 +335,7 @@
* @param sortOrder How to order the rows, formatted as an SQL ORDER BY
* clause (excluding the ORDER BY itself). Passing null will use the
* default sort order, which may be unordered.
- * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
* If the operation is canceled, then {@link OperationCanceledException} will be thrown
* when the query is executed.
* @return A Cursor object, which is positioned before the first entry, or null
@@ -343,7 +343,7 @@
*/
public final Cursor query(final Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder,
- CancelationSignal cancelationSignal) {
+ CancellationSignal cancellationSignal) {
IContentProvider provider = acquireProvider(uri);
if (provider == null) {
return null;
@@ -351,14 +351,14 @@
try {
long startTime = SystemClock.uptimeMillis();
- ICancelationSignal remoteCancelationSignal = null;
- if (cancelationSignal != null) {
- cancelationSignal.throwIfCanceled();
- remoteCancelationSignal = provider.createCancelationSignal();
- cancelationSignal.setRemote(remoteCancelationSignal);
+ ICancellationSignal remoteCancellationSignal = null;
+ if (cancellationSignal != null) {
+ cancellationSignal.throwIfCanceled();
+ remoteCancellationSignal = provider.createCancellationSignal();
+ cancellationSignal.setRemote(remoteCancellationSignal);
}
Cursor qCursor = provider.query(uri, projection,
- selection, selectionArgs, sortOrder, remoteCancelationSignal);
+ selection, selectionArgs, sortOrder, remoteCancellationSignal);
if (qCursor == null) {
releaseProvider(provider);
return null;
diff --git a/core/java/android/content/CursorLoader.java b/core/java/android/content/CursorLoader.java
index 6e4aca8..aed3728 100644
--- a/core/java/android/content/CursorLoader.java
+++ b/core/java/android/content/CursorLoader.java
@@ -48,7 +48,7 @@
String mSortOrder;
Cursor mCursor;
- CancelationSignal mCancelationSignal;
+ CancellationSignal mCancellationSignal;
/* Runs on a worker thread */
@Override
@@ -57,11 +57,11 @@
if (isLoadInBackgroundCanceled()) {
throw new OperationCanceledException();
}
- mCancelationSignal = new CancelationSignal();
+ mCancellationSignal = new CancellationSignal();
}
try {
Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection,
- mSelectionArgs, mSortOrder, mCancelationSignal);
+ mSelectionArgs, mSortOrder, mCancellationSignal);
if (cursor != null) {
// Ensure the cursor window is filled
cursor.getCount();
@@ -70,18 +70,18 @@
return cursor;
} finally {
synchronized (this) {
- mCancelationSignal = null;
+ mCancellationSignal = null;
}
}
}
@Override
- protected void onCancelLoadInBackground() {
- super.onCancelLoadInBackground();
+ public void cancelLoadInBackground() {
+ super.cancelLoadInBackground();
synchronized (this) {
- if (mCancelationSignal != null) {
- mCancelationSignal.cancel();
+ if (mCancellationSignal != null) {
+ mCancellationSignal.cancel();
}
}
}
diff --git a/core/java/android/content/ICancelationSignal.aidl b/core/java/android/content/ICancellationSignal.aidl
similarity index 95%
rename from core/java/android/content/ICancelationSignal.aidl
rename to core/java/android/content/ICancellationSignal.aidl
index 3f5a24d..cf1c5d3 100644
--- a/core/java/android/content/ICancelationSignal.aidl
+++ b/core/java/android/content/ICancellationSignal.aidl
@@ -19,6 +19,6 @@
/**
* @hide
*/
-interface ICancelationSignal {
+interface ICancellationSignal {
oneway void cancel();
}
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index f52157f..16478b7 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -34,7 +34,7 @@
*/
public interface IContentProvider extends IInterface {
public Cursor query(Uri url, String[] projection, String selection,
- String[] selectionArgs, String sortOrder, ICancelationSignal cancelationSignal)
+ String[] selectionArgs, String sortOrder, ICancellationSignal cancellationSignal)
throws RemoteException;
public String getType(Uri url) throws RemoteException;
public Uri insert(Uri url, ContentValues initialValues)
@@ -51,7 +51,7 @@
public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException;
public Bundle call(String method, String arg, Bundle extras) throws RemoteException;
- public ICancelationSignal createCancelationSignal() throws RemoteException;
+ public ICancellationSignal createCancellationSignal() throws RemoteException;
// Data interchange.
public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException;
diff --git a/core/java/android/content/Loader.java b/core/java/android/content/Loader.java
index ac05682..3052414 100644
--- a/core/java/android/content/Loader.java
+++ b/core/java/android/content/Loader.java
@@ -52,6 +52,7 @@
public class Loader<D> {
int mId;
OnLoadCompleteListener<D> mListener;
+ OnLoadCanceledListener<D> mOnLoadCanceledListener;
Context mContext;
boolean mStarted = false;
boolean mAbandoned = false;
@@ -100,6 +101,23 @@
}
/**
+ * Interface that is implemented to discover when a Loader has been canceled
+ * before it finished loading its data. You do not normally need to implement
+ * this yourself; it is used in the implementation of {@link android.app.LoaderManager}
+ * to find out when a Loader it is managing has been canceled so that it
+ * can schedule the next Loader. This interface should only be used if a
+ * Loader is not being used in conjunction with LoaderManager.
+ */
+ public interface OnLoadCanceledListener<D> {
+ /**
+ * Called on the thread that created the Loader when the load is canceled.
+ *
+ * @param loader the loader that canceled the load
+ */
+ public void onLoadCanceled(Loader<D> loader);
+ }
+
+ /**
* Stores away the application context associated with context.
* Since Loaders can be used across multiple activities it's dangerous to
* store the context directly; always use {@link #getContext()} to retrieve
@@ -127,6 +145,18 @@
}
/**
+ * Informs the registered {@link OnLoadCanceledListener} that the load has been canceled.
+ * Should only be called by subclasses.
+ *
+ * Must be called from the process's main thread.
+ */
+ public void deliverCancellation() {
+ if (mOnLoadCanceledListener != null) {
+ mOnLoadCanceledListener.onLoadCanceled(this);
+ }
+ }
+
+ /**
* @return an application context retrieved from the Context passed to the constructor.
*/
public Context getContext() {
@@ -171,6 +201,40 @@
}
/**
+ * Registers a listener that will receive callbacks when a load is canceled.
+ * The callback will be called on the process's main thread so it's safe to
+ * pass the results to widgets.
+ *
+ * Must be called from the process's main thread.
+ *
+ * @param listener The listener to register.
+ */
+ public void registerOnLoadCanceledListener(OnLoadCanceledListener<D> listener) {
+ if (mOnLoadCanceledListener != null) {
+ throw new IllegalStateException("There is already a listener registered");
+ }
+ mOnLoadCanceledListener = listener;
+ }
+
+ /**
+ * Unregisters a listener that was previously added with
+ * {@link #registerOnLoadCanceledListener}.
+ *
+ * Must be called from the process's main thread.
+ *
+ * @param listener The listener to unregister.
+ */
+ public void unregisterOnLoadCanceledListener(OnLoadCanceledListener<D> listener) {
+ if (mOnLoadCanceledListener == null) {
+ throw new IllegalStateException("No listener register");
+ }
+ if (mOnLoadCanceledListener != listener) {
+ throw new IllegalArgumentException("Attempting to unregister the wrong listener");
+ }
+ mOnLoadCanceledListener = null;
+ }
+
+ /**
* Return whether this load has been started. That is, its {@link #startLoading()}
* has been called and no calls to {@link #stopLoading()} or
* {@link #reset()} have yet been made.
@@ -234,6 +298,43 @@
}
/**
+ * Attempt to cancel the current load task.
+ * Must be called on the main thread of the process.
+ *
+ * <p>Cancellation is not an immediate operation, since the load is performed
+ * in a background thread. If there is currently a load in progress, this
+ * method requests that the load be canceled, and notes this is the case;
+ * once the background thread has completed its work its remaining state
+ * will be cleared. If another load request comes in during this time,
+ * it will be held until the canceled load is complete.
+ *
+ * @return Returns <tt>false</tt> if the task could not be canceled,
+ * typically because it has already completed normally, or
+ * because {@link #startLoading()} hasn't been called; returns
+ * <tt>true</tt> otherwise. When <tt>true</tt> is returned, the task
+ * is still running and the {@link OnLoadCanceledListener} will be called
+ * when the task completes.
+ */
+ public boolean cancelLoad() {
+ return onCancelLoad();
+ }
+
+ /**
+ * Subclasses must implement this to take care of requests to {@link #cancelLoad()}.
+ * This will always be called from the process's main thread.
+ *
+ * @return Returns <tt>false</tt> if the task could not be canceled,
+ * typically because it has already completed normally, or
+ * because {@link #startLoading()} hasn't been called; returns
+ * <tt>true</tt> otherwise. When <tt>true</tt> is returned, the task
+ * is still running and the {@link OnLoadCanceledListener} will be called
+ * when the task completes.
+ */
+ protected boolean onCancelLoad() {
+ return false;
+ }
+
+ /**
* Force an asynchronous load. Unlike {@link #startLoading()} this will ignore a previously
* loaded data set and load a new one. This simply calls through to the
* implementation's {@link #onForceLoad()}. You generally should only call this
diff --git a/core/java/android/content/OperationCanceledException.java b/core/java/android/content/OperationCanceledException.java
index 24afcfa..d783a07 100644
--- a/core/java/android/content/OperationCanceledException.java
+++ b/core/java/android/content/OperationCanceledException.java
@@ -19,7 +19,7 @@
/**
* An exception type that is thrown when an operation in progress is canceled.
*
- * @see CancelationSignal
+ * @see CancellationSignal
*/
public class OperationCanceledException extends RuntimeException {
public OperationCanceledException() {
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index decb974..bb35c29 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -364,4 +364,6 @@
VerifierDeviceIdentity getVerifierDeviceIdentity();
boolean isFirstBoot();
+
+ List<UserInfo> getUsers();
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 8541748d..26a9181 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -28,6 +28,7 @@
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.Environment;
import android.util.AndroidException;
import android.util.DisplayMetrics;
@@ -753,13 +754,6 @@
public static final int VERIFICATION_REJECT = -1;
/**
- * Range of IDs allocated for a user.
- *
- * @hide
- */
- public static final int PER_USER_RANGE = 100000;
-
- /**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device's
* audio pipeline is low-latency, more suitable for audio applications sensitive to delays or
* lag in sound input or output.
@@ -2615,39 +2609,6 @@
public abstract void updateUserFlags(int id, int flags);
/**
- * Checks to see if the user id is the same for the two uids, i.e., they belong to the same
- * user.
- * @hide
- */
- public static boolean isSameUser(int uid1, int uid2) {
- return getUserId(uid1) == getUserId(uid2);
- }
-
- /**
- * Returns the user id for a given uid.
- * @hide
- */
- public static int getUserId(int uid) {
- return uid / PER_USER_RANGE;
- }
-
- /**
- * Returns the uid that is composed from the userId and the appId.
- * @hide
- */
- public static int getUid(int userId, int appId) {
- return userId * PER_USER_RANGE + (appId % PER_USER_RANGE);
- }
-
- /**
- * Returns the app id (or base uid) for a given uid, stripping out the user id from it.
- * @hide
- */
- public static int getAppId(int uid) {
- return uid % PER_USER_RANGE;
- }
-
- /**
* Returns the device identity that verifiers can use to associate their
* scheme to a particular device. This should not be used by anything other
* than a package verifier.
@@ -2656,4 +2617,17 @@
* @hide
*/
public abstract VerifierDeviceIdentity getVerifierDeviceIdentity();
+
+ /**
+ * Returns the data directory for a particular user and package, given the uid of the package.
+ * @param uid uid of the package, including the userId and appId
+ * @param packageName name of the package
+ * @return the user-specific data directory for the package
+ * @hide
+ */
+ public static String getDataDirForUser(int userId, String packageName) {
+ // TODO: This should be shared with Installer's knowledge of user directory
+ return Environment.getDataDirectory().toString() + "/user/" + userId
+ + "/" + packageName;
+ }
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index e593d5b..faee873 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -24,18 +24,17 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
+import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.PatternMatcher;
+import android.os.UserId;
import android.util.AttributeSet;
import android.util.Base64;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Slog;
import android.util.TypedValue;
-import com.android.internal.util.XmlUtils;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
import java.io.BufferedInputStream;
import java.io.File;
@@ -59,6 +58,11 @@
import java.util.jar.JarFile;
import java.util.jar.Manifest;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
/**
* Package archive parsing
*
@@ -209,6 +213,8 @@
public static PackageInfo generatePackageInfo(PackageParser.Package p,
int gids[], int flags, long firstInstallTime, long lastUpdateTime) {
+ final int userId = Binder.getOrigCallingUser();
+
PackageInfo pi = new PackageInfo();
pi.packageName = p.packageName;
pi.versionCode = p.mVersionCode;
@@ -250,7 +256,8 @@
final Activity activity = p.activities.get(i);
if (activity.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
- pi.activities[j++] = generateActivityInfo(p.activities.get(i), flags);
+ pi.activities[j++] = generateActivityInfo(p.activities.get(i), flags,
+ userId);
}
}
}
@@ -271,7 +278,7 @@
final Activity activity = p.receivers.get(i);
if (activity.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
- pi.receivers[j++] = generateActivityInfo(p.receivers.get(i), flags);
+ pi.receivers[j++] = generateActivityInfo(p.receivers.get(i), flags, userId);
}
}
}
@@ -292,7 +299,7 @@
final Service service = p.services.get(i);
if (service.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
- pi.services[j++] = generateServiceInfo(p.services.get(i), flags);
+ pi.services[j++] = generateServiceInfo(p.services.get(i), flags, userId);
}
}
}
@@ -313,7 +320,7 @@
final Provider provider = p.providers.get(i);
if (provider.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
- pi.providers[j++] = generateProviderInfo(p.providers.get(i), flags);
+ pi.providers[j++] = generateProviderInfo(p.providers.get(i), flags, userId);
}
}
}
@@ -3241,8 +3248,12 @@
}
public static ApplicationInfo generateApplicationInfo(Package p, int flags) {
+ return generateApplicationInfo(p, flags, UserId.getUserId(Binder.getCallingUid()));
+ }
+
+ public static ApplicationInfo generateApplicationInfo(Package p, int flags, int userId) {
if (p == null) return null;
- if (!copyNeeded(flags, p, null)) {
+ if (!copyNeeded(flags, p, null) && userId == 0) {
// CompatibilityMode is global state. It's safe to modify the instance
// of the package.
if (!sCompatibilityModeEnabled) {
@@ -3258,6 +3269,10 @@
// Make shallow copy so we can store the metadata/libraries safely
ApplicationInfo ai = new ApplicationInfo(p.applicationInfo);
+ if (userId != 0) {
+ ai.uid = UserId.getUid(userId, ai.uid);
+ ai.dataDir = PackageManager.getDataDirForUser(userId, ai.packageName);
+ }
if ((flags & PackageManager.GET_META_DATA) != 0) {
ai.metaData = p.mAppMetaData;
}
@@ -3325,16 +3340,15 @@
}
}
- public static final ActivityInfo generateActivityInfo(Activity a,
- int flags) {
+ public static final ActivityInfo generateActivityInfo(Activity a, int flags, int userId) {
if (a == null) return null;
- if (!copyNeeded(flags, a.owner, a.metaData)) {
+ if (!copyNeeded(flags, a.owner, a.metaData) && userId == 0) {
return a.info;
}
// Make shallow copies so we can store the metadata safely
ActivityInfo ai = new ActivityInfo(a.info);
ai.metaData = a.metaData;
- ai.applicationInfo = generateApplicationInfo(a.owner, flags);
+ ai.applicationInfo = generateApplicationInfo(a.owner, flags, userId);
return ai;
}
@@ -3359,15 +3373,15 @@
}
}
- public static final ServiceInfo generateServiceInfo(Service s, int flags) {
+ public static final ServiceInfo generateServiceInfo(Service s, int flags, int userId) {
if (s == null) return null;
- if (!copyNeeded(flags, s.owner, s.metaData)) {
+ if (!copyNeeded(flags, s.owner, s.metaData) && userId == 0) {
return s.info;
}
// Make shallow copies so we can store the metadata safely
ServiceInfo si = new ServiceInfo(s.info);
si.metaData = s.metaData;
- si.applicationInfo = generateApplicationInfo(s.owner, flags);
+ si.applicationInfo = generateApplicationInfo(s.owner, flags, userId);
return si;
}
@@ -3400,12 +3414,12 @@
}
}
- public static final ProviderInfo generateProviderInfo(Provider p,
- int flags) {
+ public static final ProviderInfo generateProviderInfo(Provider p, int flags, int userId) {
if (p == null) return null;
if (!copyNeeded(flags, p.owner, p.metaData)
&& ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) != 0
- || p.info.uriPermissionPatterns == null)) {
+ || p.info.uriPermissionPatterns == null)
+ && userId == 0) {
return p.info;
}
// Make shallow copies so we can store the metadata safely
@@ -3414,7 +3428,7 @@
if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) {
pi.uriPermissionPatterns = null;
}
- pi.applicationInfo = generateApplicationInfo(p.owner, flags);
+ pi.applicationInfo = generateApplicationInfo(p.owner, flags, userId);
return pi;
}
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index b69d9bf..0022118 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -21,6 +21,7 @@
import android.content.ContentValues;
import android.content.Context;
import android.content.OperationApplicationException;
+import android.content.OperationCanceledException;
import android.database.sqlite.SQLiteAbortException;
import android.database.sqlite.SQLiteConstraintException;
import android.database.sqlite.SQLiteDatabase;
@@ -107,6 +108,9 @@
code = 9;
} else if (e instanceof OperationApplicationException) {
code = 10;
+ } else if (e instanceof OperationCanceledException) {
+ code = 11;
+ logException = false;
} else {
reply.writeException(e);
Log.e(TAG, "Writing exception to parcel", e);
@@ -178,6 +182,8 @@
throw new SQLiteDiskIOException(msg);
case 9:
throw new SQLiteException(msg);
+ case 11:
+ throw new OperationCanceledException(msg);
default:
reply.readException(code, msg);
}
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 710bd53..b5cef81 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -19,7 +19,7 @@
import dalvik.system.BlockGuard;
import dalvik.system.CloseGuard;
-import android.content.CancelationSignal;
+import android.content.CancellationSignal;
import android.content.OperationCanceledException;
import android.database.Cursor;
import android.database.CursorWindow;
@@ -84,7 +84,7 @@
*
* @hide
*/
-public final class SQLiteConnection implements CancelationSignal.OnCancelListener {
+public final class SQLiteConnection implements CancellationSignal.OnCancelListener {
private static final String TAG = "SQLiteConnection";
private static final boolean DEBUG = false;
@@ -110,11 +110,11 @@
private boolean mOnlyAllowReadOnlyOperations;
- // The number of times attachCancelationSignal has been called.
+ // The number of times attachCancellationSignal has been called.
// Because SQLite statement execution can be re-entrant, we keep track of how many
- // times we have attempted to attach a cancelation signal to the connection so that
+ // times we have attempted to attach a cancellation signal to the connection so that
// we can ensure that we detach the signal at the right time.
- private int mCancelationSignalAttachCount;
+ private int mCancellationSignalAttachCount;
private static native int nativeOpen(String path, int openFlags, String label,
boolean enableTrace, boolean enableProfile);
@@ -355,14 +355,14 @@
*
* @param sql The SQL statement to execute.
* @param bindArgs The arguments to bind, or null if none.
- * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
*
* @throws SQLiteException if an error occurs, such as a syntax error
* or invalid number of bind arguments.
* @throws OperationCanceledException if the operation was canceled.
*/
public void execute(String sql, Object[] bindArgs,
- CancelationSignal cancelationSignal) {
+ CancellationSignal cancellationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
@@ -374,11 +374,11 @@
throwIfStatementForbidden(statement);
bindArguments(statement, bindArgs);
applyBlockGuardPolicy(statement);
- attachCancelationSignal(cancelationSignal);
+ attachCancellationSignal(cancellationSignal);
try {
nativeExecute(mConnectionPtr, statement.mStatementPtr);
} finally {
- detachCancelationSignal(cancelationSignal);
+ detachCancellationSignal(cancellationSignal);
}
} finally {
releasePreparedStatement(statement);
@@ -396,7 +396,7 @@
*
* @param sql The SQL statement to execute.
* @param bindArgs The arguments to bind, or null if none.
- * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
* @return The value of the first column in the first row of the result set
* as a <code>long</code>, or zero if none.
*
@@ -405,7 +405,7 @@
* @throws OperationCanceledException if the operation was canceled.
*/
public long executeForLong(String sql, Object[] bindArgs,
- CancelationSignal cancelationSignal) {
+ CancellationSignal cancellationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
@@ -417,11 +417,11 @@
throwIfStatementForbidden(statement);
bindArguments(statement, bindArgs);
applyBlockGuardPolicy(statement);
- attachCancelationSignal(cancelationSignal);
+ attachCancellationSignal(cancellationSignal);
try {
return nativeExecuteForLong(mConnectionPtr, statement.mStatementPtr);
} finally {
- detachCancelationSignal(cancelationSignal);
+ detachCancellationSignal(cancellationSignal);
}
} finally {
releasePreparedStatement(statement);
@@ -439,7 +439,7 @@
*
* @param sql The SQL statement to execute.
* @param bindArgs The arguments to bind, or null if none.
- * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
* @return The value of the first column in the first row of the result set
* as a <code>String</code>, or null if none.
*
@@ -448,7 +448,7 @@
* @throws OperationCanceledException if the operation was canceled.
*/
public String executeForString(String sql, Object[] bindArgs,
- CancelationSignal cancelationSignal) {
+ CancellationSignal cancellationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
@@ -460,11 +460,11 @@
throwIfStatementForbidden(statement);
bindArguments(statement, bindArgs);
applyBlockGuardPolicy(statement);
- attachCancelationSignal(cancelationSignal);
+ attachCancellationSignal(cancellationSignal);
try {
return nativeExecuteForString(mConnectionPtr, statement.mStatementPtr);
} finally {
- detachCancelationSignal(cancelationSignal);
+ detachCancellationSignal(cancellationSignal);
}
} finally {
releasePreparedStatement(statement);
@@ -483,7 +483,7 @@
*
* @param sql The SQL statement to execute.
* @param bindArgs The arguments to bind, or null if none.
- * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
* @return The file descriptor for a shared memory region that contains
* the value of the first column in the first row of the result set as a BLOB,
* or null if none.
@@ -493,7 +493,7 @@
* @throws OperationCanceledException if the operation was canceled.
*/
public ParcelFileDescriptor executeForBlobFileDescriptor(String sql, Object[] bindArgs,
- CancelationSignal cancelationSignal) {
+ CancellationSignal cancellationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
@@ -506,13 +506,13 @@
throwIfStatementForbidden(statement);
bindArguments(statement, bindArgs);
applyBlockGuardPolicy(statement);
- attachCancelationSignal(cancelationSignal);
+ attachCancellationSignal(cancellationSignal);
try {
int fd = nativeExecuteForBlobFileDescriptor(
mConnectionPtr, statement.mStatementPtr);
return fd >= 0 ? ParcelFileDescriptor.adoptFd(fd) : null;
} finally {
- detachCancelationSignal(cancelationSignal);
+ detachCancellationSignal(cancellationSignal);
}
} finally {
releasePreparedStatement(statement);
@@ -531,7 +531,7 @@
*
* @param sql The SQL statement to execute.
* @param bindArgs The arguments to bind, or null if none.
- * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
* @return The number of rows that were changed.
*
* @throws SQLiteException if an error occurs, such as a syntax error
@@ -539,7 +539,7 @@
* @throws OperationCanceledException if the operation was canceled.
*/
public int executeForChangedRowCount(String sql, Object[] bindArgs,
- CancelationSignal cancelationSignal) {
+ CancellationSignal cancellationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
@@ -552,12 +552,12 @@
throwIfStatementForbidden(statement);
bindArguments(statement, bindArgs);
applyBlockGuardPolicy(statement);
- attachCancelationSignal(cancelationSignal);
+ attachCancellationSignal(cancellationSignal);
try {
return nativeExecuteForChangedRowCount(
mConnectionPtr, statement.mStatementPtr);
} finally {
- detachCancelationSignal(cancelationSignal);
+ detachCancellationSignal(cancellationSignal);
}
} finally {
releasePreparedStatement(statement);
@@ -576,7 +576,7 @@
*
* @param sql The SQL statement to execute.
* @param bindArgs The arguments to bind, or null if none.
- * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
* @return The row id of the last row that was inserted, or 0 if none.
*
* @throws SQLiteException if an error occurs, such as a syntax error
@@ -584,7 +584,7 @@
* @throws OperationCanceledException if the operation was canceled.
*/
public long executeForLastInsertedRowId(String sql, Object[] bindArgs,
- CancelationSignal cancelationSignal) {
+ CancellationSignal cancellationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
@@ -597,12 +597,12 @@
throwIfStatementForbidden(statement);
bindArguments(statement, bindArgs);
applyBlockGuardPolicy(statement);
- attachCancelationSignal(cancelationSignal);
+ attachCancellationSignal(cancellationSignal);
try {
return nativeExecuteForLastInsertedRowId(
mConnectionPtr, statement.mStatementPtr);
} finally {
- detachCancelationSignal(cancelationSignal);
+ detachCancellationSignal(cancellationSignal);
}
} finally {
releasePreparedStatement(statement);
@@ -629,7 +629,7 @@
* so that it does. Must be greater than or equal to <code>startPos</code>.
* @param countAllRows True to count all rows that the query would return
* regagless of whether they fit in the window.
- * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
* @return The number of rows that were counted during query execution. Might
* not be all rows in the result set unless <code>countAllRows</code> is true.
*
@@ -639,7 +639,7 @@
*/
public int executeForCursorWindow(String sql, Object[] bindArgs,
CursorWindow window, int startPos, int requiredPos, boolean countAllRows,
- CancelationSignal cancelationSignal) {
+ CancellationSignal cancellationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
@@ -658,7 +658,7 @@
throwIfStatementForbidden(statement);
bindArguments(statement, bindArgs);
applyBlockGuardPolicy(statement);
- attachCancelationSignal(cancelationSignal);
+ attachCancellationSignal(cancellationSignal);
try {
final long result = nativeExecuteForCursorWindow(
mConnectionPtr, statement.mStatementPtr, window.mWindowPtr,
@@ -669,7 +669,7 @@
window.setStartPosition(actualPos);
return countedRows;
} finally {
- detachCancelationSignal(cancelationSignal);
+ detachCancellationSignal(cancellationSignal);
}
} finally {
releasePreparedStatement(statement);
@@ -751,40 +751,40 @@
recyclePreparedStatement(statement);
}
- private void attachCancelationSignal(CancelationSignal cancelationSignal) {
- if (cancelationSignal != null) {
- cancelationSignal.throwIfCanceled();
+ private void attachCancellationSignal(CancellationSignal cancellationSignal) {
+ if (cancellationSignal != null) {
+ cancellationSignal.throwIfCanceled();
- mCancelationSignalAttachCount += 1;
- if (mCancelationSignalAttachCount == 1) {
- // Reset cancelation flag before executing the statement.
+ mCancellationSignalAttachCount += 1;
+ if (mCancellationSignalAttachCount == 1) {
+ // Reset cancellation flag before executing the statement.
nativeResetCancel(mConnectionPtr, true /*cancelable*/);
// After this point, onCancel() may be called concurrently.
- cancelationSignal.setOnCancelListener(this);
+ cancellationSignal.setOnCancelListener(this);
}
}
}
- private void detachCancelationSignal(CancelationSignal cancelationSignal) {
- if (cancelationSignal != null) {
- assert mCancelationSignalAttachCount > 0;
+ private void detachCancellationSignal(CancellationSignal cancellationSignal) {
+ if (cancellationSignal != null) {
+ assert mCancellationSignalAttachCount > 0;
- mCancelationSignalAttachCount -= 1;
- if (mCancelationSignalAttachCount == 0) {
+ mCancellationSignalAttachCount -= 1;
+ if (mCancellationSignalAttachCount == 0) {
// After this point, onCancel() cannot be called concurrently.
- cancelationSignal.setOnCancelListener(null);
+ cancellationSignal.setOnCancelListener(null);
- // Reset cancelation flag after executing the statement.
+ // Reset cancellation flag after executing the statement.
nativeResetCancel(mConnectionPtr, false /*cancelable*/);
}
}
}
- // CancelationSignal.OnCancelationListener callback.
+ // CancellationSignal.OnCancelListener callback.
// This method may be called on a different thread than the executing statement.
- // However, it will only be called between calls to attachCancelationSignal and
- // detachCancelationSignal, while a statement is executing. We can safely assume
+ // However, it will only be called between calls to attachCancellationSignal and
+ // detachCancellationSignal, while a statement is executing. We can safely assume
// that the SQLite connection is still alive.
@Override
public void onCancel() {
diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java
index d335738..236948e 100644
--- a/core/java/android/database/sqlite/SQLiteConnectionPool.java
+++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java
@@ -18,7 +18,7 @@
import dalvik.system.CloseGuard;
-import android.content.CancelationSignal;
+import android.content.CancellationSignal;
import android.content.OperationCanceledException;
import android.database.sqlite.SQLiteDebug.DbStats;
import android.os.SystemClock;
@@ -284,7 +284,7 @@
* @param sql If not null, try to find a connection that already has
* the specified SQL statement in its prepared statement cache.
* @param connectionFlags The connection request flags.
- * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
* @return The connection that was acquired, never null.
*
* @throws IllegalStateException if the pool has been closed.
@@ -292,8 +292,8 @@
* @throws OperationCanceledException if the operation was canceled.
*/
public SQLiteConnection acquireConnection(String sql, int connectionFlags,
- CancelationSignal cancelationSignal) {
- return waitForConnection(sql, connectionFlags, cancelationSignal);
+ CancellationSignal cancellationSignal) {
+ return waitForConnection(sql, connectionFlags, cancellationSignal);
}
/**
@@ -503,7 +503,7 @@
// Might throw.
private SQLiteConnection waitForConnection(String sql, int connectionFlags,
- CancelationSignal cancelationSignal) {
+ CancellationSignal cancellationSignal) {
final boolean wantPrimaryConnection =
(connectionFlags & CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY) != 0;
@@ -512,8 +512,8 @@
throwIfClosedLocked();
// Abort if canceled.
- if (cancelationSignal != null) {
- cancelationSignal.throwIfCanceled();
+ if (cancellationSignal != null) {
+ cancellationSignal.throwIfCanceled();
}
// Try to acquire a connection.
@@ -550,9 +550,9 @@
mConnectionWaiterQueue = waiter;
}
- if (cancelationSignal != null) {
+ if (cancellationSignal != null) {
final int nonce = waiter.mNonce;
- cancelationSignal.setOnCancelListener(new CancelationSignal.OnCancelListener() {
+ cancellationSignal.setOnCancelListener(new CancellationSignal.OnCancelListener() {
@Override
public void onCancel() {
synchronized (mLock) {
@@ -588,8 +588,8 @@
final SQLiteConnection connection = waiter.mAssignedConnection;
final RuntimeException ex = waiter.mException;
if (connection != null || ex != null) {
- if (cancelationSignal != null) {
- cancelationSignal.setOnCancelListener(null);
+ if (cancellationSignal != null) {
+ cancellationSignal.setOnCancelListener(null);
}
recycleConnectionWaiterLocked(waiter);
if (connection != null) {
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 7db7bfb..505f83e 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -16,7 +16,7 @@
package android.database.sqlite;
-import android.content.CancelationSignal;
+import android.content.CancellationSignal;
import android.content.ContentValues;
import android.content.OperationCanceledException;
import android.content.res.Resources;
@@ -967,7 +967,7 @@
* default sort order, which may be unordered.
* @param limit Limits the number of rows returned by the query,
* formatted as LIMIT clause. Passing null denotes no LIMIT clause.
- * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
* If the operation is canceled, then {@link OperationCanceledException} will be thrown
* when the query is executed.
* @return A {@link Cursor} object, which is positioned before the first entry. Note that
@@ -976,9 +976,9 @@
*/
public Cursor query(boolean distinct, String table, String[] columns,
String selection, String[] selectionArgs, String groupBy,
- String having, String orderBy, String limit, CancelationSignal cancelationSignal) {
+ String having, String orderBy, String limit, CancellationSignal cancellationSignal) {
return queryWithFactory(null, distinct, table, columns, selection, selectionArgs,
- groupBy, having, orderBy, limit, cancelationSignal);
+ groupBy, having, orderBy, limit, cancellationSignal);
}
/**
@@ -1049,7 +1049,7 @@
* default sort order, which may be unordered.
* @param limit Limits the number of rows returned by the query,
* formatted as LIMIT clause. Passing null denotes no LIMIT clause.
- * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
* If the operation is canceled, then {@link OperationCanceledException} will be thrown
* when the query is executed.
* @return A {@link Cursor} object, which is positioned before the first entry. Note that
@@ -1059,13 +1059,13 @@
public Cursor queryWithFactory(CursorFactory cursorFactory,
boolean distinct, String table, String[] columns,
String selection, String[] selectionArgs, String groupBy,
- String having, String orderBy, String limit, CancelationSignal cancelationSignal) {
+ String having, String orderBy, String limit, CancellationSignal cancellationSignal) {
throwIfNotOpen(); // fail fast
String sql = SQLiteQueryBuilder.buildQueryString(
distinct, table, columns, selection, groupBy, having, orderBy, limit);
return rawQueryWithFactory(cursorFactory, sql, selectionArgs,
- findEditTable(table), cancelationSignal);
+ findEditTable(table), cancellationSignal);
}
/**
@@ -1163,15 +1163,15 @@
* @param selectionArgs You may include ?s in where clause in the query,
* which will be replaced by the values from selectionArgs. The
* values will be bound as Strings.
- * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
* If the operation is canceled, then {@link OperationCanceledException} will be thrown
* when the query is executed.
* @return A {@link Cursor} object, which is positioned before the first entry. Note that
* {@link Cursor}s are not synchronized, see the documentation for more details.
*/
public Cursor rawQuery(String sql, String[] selectionArgs,
- CancelationSignal cancelationSignal) {
- return rawQueryWithFactory(null, sql, selectionArgs, null, cancelationSignal);
+ CancellationSignal cancellationSignal) {
+ return rawQueryWithFactory(null, sql, selectionArgs, null, cancellationSignal);
}
/**
@@ -1201,7 +1201,7 @@
* which will be replaced by the values from selectionArgs. The
* values will be bound as Strings.
* @param editTable the name of the first table, which is editable
- * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
* If the operation is canceled, then {@link OperationCanceledException} will be thrown
* when the query is executed.
* @return A {@link Cursor} object, which is positioned before the first entry. Note that
@@ -1209,11 +1209,11 @@
*/
public Cursor rawQueryWithFactory(
CursorFactory cursorFactory, String sql, String[] selectionArgs,
- String editTable, CancelationSignal cancelationSignal) {
+ String editTable, CancellationSignal cancellationSignal) {
throwIfNotOpen(); // fail fast
SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable,
- cancelationSignal);
+ cancellationSignal);
return driver.query(cursorFactory != null ? cursorFactory : mCursorFactory,
selectionArgs);
}
diff --git a/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java b/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java
index c490dc6..3375e74 100644
--- a/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java
+++ b/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java
@@ -16,7 +16,7 @@
package android.database.sqlite;
-import android.content.CancelationSignal;
+import android.content.CancellationSignal;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
@@ -29,19 +29,19 @@
private final SQLiteDatabase mDatabase;
private final String mEditTable;
private final String mSql;
- private final CancelationSignal mCancelationSignal;
+ private final CancellationSignal mCancellationSignal;
private SQLiteQuery mQuery;
public SQLiteDirectCursorDriver(SQLiteDatabase db, String sql, String editTable,
- CancelationSignal cancelationSignal) {
+ CancellationSignal cancellationSignal) {
mDatabase = db;
mEditTable = editTable;
mSql = sql;
- mCancelationSignal = cancelationSignal;
+ mCancellationSignal = cancellationSignal;
}
public Cursor query(CursorFactory factory, String[] selectionArgs) {
- final SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, mCancelationSignal);
+ final SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, mCancellationSignal);
final Cursor cursor;
try {
query.bindAllArgsAsStrings(selectionArgs);
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index f3da2a6..9f0edfb 100644
--- a/core/java/android/database/sqlite/SQLiteProgram.java
+++ b/core/java/android/database/sqlite/SQLiteProgram.java
@@ -16,7 +16,7 @@
package android.database.sqlite;
-import android.content.CancelationSignal;
+import android.content.CancellationSignal;
import android.database.DatabaseUtils;
import java.util.Arrays;
@@ -38,7 +38,7 @@
private final Object[] mBindArgs;
SQLiteProgram(SQLiteDatabase db, String sql, Object[] bindArgs,
- CancelationSignal cancelationSignalForPrepare) {
+ CancellationSignal cancellationSignalForPrepare) {
mDatabase = db;
mSql = sql.trim();
@@ -57,7 +57,7 @@
SQLiteStatementInfo info = new SQLiteStatementInfo();
db.getThreadSession().prepare(mSql,
db.getThreadDefaultConnectionFlags(assumeReadOnly),
- cancelationSignalForPrepare, info);
+ cancellationSignalForPrepare, info);
mReadOnly = info.readOnly;
mColumnNames = info.columnNames;
mNumParameters = info.numParameters;
diff --git a/core/java/android/database/sqlite/SQLiteQuery.java b/core/java/android/database/sqlite/SQLiteQuery.java
index df2e260..30e77b5 100644
--- a/core/java/android/database/sqlite/SQLiteQuery.java
+++ b/core/java/android/database/sqlite/SQLiteQuery.java
@@ -16,7 +16,7 @@
package android.database.sqlite;
-import android.content.CancelationSignal;
+import android.content.CancellationSignal;
import android.content.OperationCanceledException;
import android.database.CursorWindow;
import android.util.Log;
@@ -31,12 +31,12 @@
public final class SQLiteQuery extends SQLiteProgram {
private static final String TAG = "SQLiteQuery";
- private final CancelationSignal mCancelationSignal;
+ private final CancellationSignal mCancellationSignal;
- SQLiteQuery(SQLiteDatabase db, String query, CancelationSignal cancelationSignal) {
- super(db, query, null, cancelationSignal);
+ SQLiteQuery(SQLiteDatabase db, String query, CancellationSignal cancellationSignal) {
+ super(db, query, null, cancellationSignal);
- mCancelationSignal = cancelationSignal;
+ mCancellationSignal = cancellationSignal;
}
/**
@@ -61,7 +61,7 @@
try {
int numRows = getSession().executeForCursorWindow(getSql(), getBindArgs(),
window, startPos, requiredPos, countAllRows, getConnectionFlags(),
- mCancelationSignal);
+ mCancellationSignal);
return numRows;
} catch (SQLiteDatabaseCorruptException ex) {
onCorruption();
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index 89469cb..6f84b5e 100644
--- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
@@ -16,7 +16,7 @@
package android.database.sqlite;
-import android.content.CancelationSignal;
+import android.content.CancellationSignal;
import android.content.OperationCanceledException;
import android.database.Cursor;
import android.database.DatabaseUtils;
@@ -292,7 +292,7 @@
String selection, String[] selectionArgs, String groupBy,
String having, String sortOrder) {
return query(db, projectionIn, selection, selectionArgs, groupBy, having, sortOrder,
- null /* limit */, null /* cancelationSignal */);
+ null /* limit */, null /* cancellationSignal */);
}
/**
@@ -362,7 +362,7 @@
* will use the default sort order, which may be unordered.
* @param limit Limits the number of rows returned by the query,
* formatted as LIMIT clause. Passing null denotes no LIMIT clause.
- * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
* If the operation is canceled, then {@link OperationCanceledException} will be thrown
* when the query is executed.
* @return a cursor over the result set
@@ -371,7 +371,7 @@
*/
public Cursor query(SQLiteDatabase db, String[] projectionIn,
String selection, String[] selectionArgs, String groupBy,
- String having, String sortOrder, String limit, CancelationSignal cancelationSignal) {
+ String having, String sortOrder, String limit, CancellationSignal cancellationSignal) {
if (mTables == null) {
return null;
}
@@ -387,7 +387,7 @@
String sqlForValidation = buildQuery(projectionIn, "(" + selection + ")", groupBy,
having, sortOrder, limit);
validateQuerySql(db, sqlForValidation,
- cancelationSignal); // will throw if query is invalid
+ cancellationSignal); // will throw if query is invalid
}
String sql = buildQuery(
@@ -400,7 +400,7 @@
return db.rawQueryWithFactory(
mFactory, sql, selectionArgs,
SQLiteDatabase.findEditTable(mTables),
- cancelationSignal); // will throw if query is invalid
+ cancellationSignal); // will throw if query is invalid
}
/**
@@ -408,9 +408,9 @@
* If the SQL statement is not valid, this method will throw a {@link SQLiteException}.
*/
private void validateQuerySql(SQLiteDatabase db, String sql,
- CancelationSignal cancelationSignal) {
+ CancellationSignal cancellationSignal) {
db.getThreadSession().prepare(sql,
- db.getThreadDefaultConnectionFlags(true /*readOnly*/), cancelationSignal, null);
+ db.getThreadDefaultConnectionFlags(true /*readOnly*/), cancellationSignal, null);
}
/**
diff --git a/core/java/android/database/sqlite/SQLiteSession.java b/core/java/android/database/sqlite/SQLiteSession.java
index b5a3e31..43efb03 100644
--- a/core/java/android/database/sqlite/SQLiteSession.java
+++ b/core/java/android/database/sqlite/SQLiteSession.java
@@ -16,7 +16,7 @@
package android.database.sqlite;
-import android.content.CancelationSignal;
+import android.content.CancellationSignal;
import android.content.OperationCanceledException;
import android.database.CursorWindow;
import android.database.DatabaseUtils;
@@ -280,7 +280,7 @@
* @param transactionListener The transaction listener, or null if none.
* @param connectionFlags The connection flags to use if a connection must be
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
- * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
*
* @throws IllegalStateException if {@link #setTransactionSuccessful} has already been
* called for the current transaction.
@@ -293,21 +293,21 @@
*/
public void beginTransaction(int transactionMode,
SQLiteTransactionListener transactionListener, int connectionFlags,
- CancelationSignal cancelationSignal) {
+ CancellationSignal cancellationSignal) {
throwIfTransactionMarkedSuccessful();
beginTransactionUnchecked(transactionMode, transactionListener, connectionFlags,
- cancelationSignal);
+ cancellationSignal);
}
private void beginTransactionUnchecked(int transactionMode,
SQLiteTransactionListener transactionListener, int connectionFlags,
- CancelationSignal cancelationSignal) {
- if (cancelationSignal != null) {
- cancelationSignal.throwIfCanceled();
+ CancellationSignal cancellationSignal) {
+ if (cancellationSignal != null) {
+ cancellationSignal.throwIfCanceled();
}
if (mTransactionStack == null) {
- acquireConnection(null, connectionFlags, cancelationSignal); // might throw
+ acquireConnection(null, connectionFlags, cancellationSignal); // might throw
}
try {
// Set up the transaction such that we can back out safely
@@ -317,14 +317,14 @@
switch (transactionMode) {
case TRANSACTION_MODE_IMMEDIATE:
mConnection.execute("BEGIN IMMEDIATE;", null,
- cancelationSignal); // might throw
+ cancellationSignal); // might throw
break;
case TRANSACTION_MODE_EXCLUSIVE:
mConnection.execute("BEGIN EXCLUSIVE;", null,
- cancelationSignal); // might throw
+ cancellationSignal); // might throw
break;
default:
- mConnection.execute("BEGIN;", null, cancelationSignal); // might throw
+ mConnection.execute("BEGIN;", null, cancellationSignal); // might throw
break;
}
}
@@ -335,7 +335,7 @@
transactionListener.onBegin(); // might throw
} catch (RuntimeException ex) {
if (mTransactionStack == null) {
- mConnection.execute("ROLLBACK;", null, cancelationSignal); // might throw
+ mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw
}
throw ex;
}
@@ -384,7 +384,7 @@
* This method must be called exactly once for each call to {@link #beginTransaction}.
* </p>
*
- * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
*
* @throws IllegalStateException if there is no current transaction.
* @throws SQLiteException if an error occurs.
@@ -394,16 +394,16 @@
* @see #setTransactionSuccessful
* @see #yieldTransaction
*/
- public void endTransaction(CancelationSignal cancelationSignal) {
+ public void endTransaction(CancellationSignal cancellationSignal) {
throwIfNoTransaction();
assert mConnection != null;
- endTransactionUnchecked(cancelationSignal);
+ endTransactionUnchecked(cancellationSignal);
}
- private void endTransactionUnchecked(CancelationSignal cancelationSignal) {
- if (cancelationSignal != null) {
- cancelationSignal.throwIfCanceled();
+ private void endTransactionUnchecked(CancellationSignal cancellationSignal) {
+ if (cancellationSignal != null) {
+ cancellationSignal.throwIfCanceled();
}
final Transaction top = mTransactionStack;
@@ -434,9 +434,9 @@
} else {
try {
if (successful) {
- mConnection.execute("COMMIT;", null, cancelationSignal); // might throw
+ mConnection.execute("COMMIT;", null, cancellationSignal); // might throw
} else {
- mConnection.execute("ROLLBACK;", null, cancelationSignal); // might throw
+ mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw
}
} finally {
releaseConnection(); // might throw
@@ -487,7 +487,7 @@
* @param throwIfUnsafe If true, then instead of returning false when no
* transaction is in progress, a nested transaction is in progress, or when
* the transaction has already been marked successful, throws {@link IllegalStateException}.
- * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
* @return True if the transaction was actually yielded.
*
* @throws IllegalStateException if <code>throwIfNested</code> is true and
@@ -500,7 +500,7 @@
* @see #endTransaction
*/
public boolean yieldTransaction(long sleepAfterYieldDelayMillis, boolean throwIfUnsafe,
- CancelationSignal cancelationSignal) {
+ CancellationSignal cancellationSignal) {
if (throwIfUnsafe) {
throwIfNoTransaction();
throwIfTransactionMarkedSuccessful();
@@ -518,13 +518,13 @@
}
return yieldTransactionUnchecked(sleepAfterYieldDelayMillis,
- cancelationSignal); // might throw
+ cancellationSignal); // might throw
}
private boolean yieldTransactionUnchecked(long sleepAfterYieldDelayMillis,
- CancelationSignal cancelationSignal) {
- if (cancelationSignal != null) {
- cancelationSignal.throwIfCanceled();
+ CancellationSignal cancellationSignal) {
+ if (cancellationSignal != null) {
+ cancellationSignal.throwIfCanceled();
}
if (!mConnectionPool.shouldYieldConnection(mConnection, mConnectionFlags)) {
@@ -534,7 +534,7 @@
final int transactionMode = mTransactionStack.mMode;
final SQLiteTransactionListener listener = mTransactionStack.mListener;
final int connectionFlags = mConnectionFlags;
- endTransactionUnchecked(cancelationSignal); // might throw
+ endTransactionUnchecked(cancellationSignal); // might throw
if (sleepAfterYieldDelayMillis > 0) {
try {
@@ -545,7 +545,7 @@
}
beginTransactionUnchecked(transactionMode, listener, connectionFlags,
- cancelationSignal); // might throw
+ cancellationSignal); // might throw
return true;
}
@@ -566,24 +566,24 @@
* @param sql The SQL statement to prepare.
* @param connectionFlags The connection flags to use if a connection must be
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
- * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
* @param outStatementInfo The {@link SQLiteStatementInfo} object to populate
* with information about the statement, or null if none.
*
* @throws SQLiteException if an error occurs, such as a syntax error.
* @throws OperationCanceledException if the operation was canceled.
*/
- public void prepare(String sql, int connectionFlags, CancelationSignal cancelationSignal,
+ public void prepare(String sql, int connectionFlags, CancellationSignal cancellationSignal,
SQLiteStatementInfo outStatementInfo) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
- if (cancelationSignal != null) {
- cancelationSignal.throwIfCanceled();
+ if (cancellationSignal != null) {
+ cancellationSignal.throwIfCanceled();
}
- acquireConnection(sql, connectionFlags, cancelationSignal); // might throw
+ acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
try {
mConnection.prepare(sql, outStatementInfo); // might throw
} finally {
@@ -598,25 +598,25 @@
* @param bindArgs The arguments to bind, or null if none.
* @param connectionFlags The connection flags to use if a connection must be
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
- * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
*
* @throws SQLiteException if an error occurs, such as a syntax error
* or invalid number of bind arguments.
* @throws OperationCanceledException if the operation was canceled.
*/
public void execute(String sql, Object[] bindArgs, int connectionFlags,
- CancelationSignal cancelationSignal) {
+ CancellationSignal cancellationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
- if (executeSpecial(sql, bindArgs, connectionFlags, cancelationSignal)) {
+ if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
return;
}
- acquireConnection(sql, connectionFlags, cancelationSignal); // might throw
+ acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
try {
- mConnection.execute(sql, bindArgs, cancelationSignal); // might throw
+ mConnection.execute(sql, bindArgs, cancellationSignal); // might throw
} finally {
releaseConnection(); // might throw
}
@@ -629,7 +629,7 @@
* @param bindArgs The arguments to bind, or null if none.
* @param connectionFlags The connection flags to use if a connection must be
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
- * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
* @return The value of the first column in the first row of the result set
* as a <code>long</code>, or zero if none.
*
@@ -638,18 +638,18 @@
* @throws OperationCanceledException if the operation was canceled.
*/
public long executeForLong(String sql, Object[] bindArgs, int connectionFlags,
- CancelationSignal cancelationSignal) {
+ CancellationSignal cancellationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
- if (executeSpecial(sql, bindArgs, connectionFlags, cancelationSignal)) {
+ if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
return 0;
}
- acquireConnection(sql, connectionFlags, cancelationSignal); // might throw
+ acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
try {
- return mConnection.executeForLong(sql, bindArgs, cancelationSignal); // might throw
+ return mConnection.executeForLong(sql, bindArgs, cancellationSignal); // might throw
} finally {
releaseConnection(); // might throw
}
@@ -662,7 +662,7 @@
* @param bindArgs The arguments to bind, or null if none.
* @param connectionFlags The connection flags to use if a connection must be
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
- * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
* @return The value of the first column in the first row of the result set
* as a <code>String</code>, or null if none.
*
@@ -671,18 +671,18 @@
* @throws OperationCanceledException if the operation was canceled.
*/
public String executeForString(String sql, Object[] bindArgs, int connectionFlags,
- CancelationSignal cancelationSignal) {
+ CancellationSignal cancellationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
- if (executeSpecial(sql, bindArgs, connectionFlags, cancelationSignal)) {
+ if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
return null;
}
- acquireConnection(sql, connectionFlags, cancelationSignal); // might throw
+ acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
try {
- return mConnection.executeForString(sql, bindArgs, cancelationSignal); // might throw
+ return mConnection.executeForString(sql, bindArgs, cancellationSignal); // might throw
} finally {
releaseConnection(); // might throw
}
@@ -696,7 +696,7 @@
* @param bindArgs The arguments to bind, or null if none.
* @param connectionFlags The connection flags to use if a connection must be
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
- * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
* @return The file descriptor for a shared memory region that contains
* the value of the first column in the first row of the result set as a BLOB,
* or null if none.
@@ -706,19 +706,19 @@
* @throws OperationCanceledException if the operation was canceled.
*/
public ParcelFileDescriptor executeForBlobFileDescriptor(String sql, Object[] bindArgs,
- int connectionFlags, CancelationSignal cancelationSignal) {
+ int connectionFlags, CancellationSignal cancellationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
- if (executeSpecial(sql, bindArgs, connectionFlags, cancelationSignal)) {
+ if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
return null;
}
- acquireConnection(sql, connectionFlags, cancelationSignal); // might throw
+ acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
try {
return mConnection.executeForBlobFileDescriptor(sql, bindArgs,
- cancelationSignal); // might throw
+ cancellationSignal); // might throw
} finally {
releaseConnection(); // might throw
}
@@ -732,7 +732,7 @@
* @param bindArgs The arguments to bind, or null if none.
* @param connectionFlags The connection flags to use if a connection must be
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
- * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
* @return The number of rows that were changed.
*
* @throws SQLiteException if an error occurs, such as a syntax error
@@ -740,19 +740,19 @@
* @throws OperationCanceledException if the operation was canceled.
*/
public int executeForChangedRowCount(String sql, Object[] bindArgs, int connectionFlags,
- CancelationSignal cancelationSignal) {
+ CancellationSignal cancellationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
- if (executeSpecial(sql, bindArgs, connectionFlags, cancelationSignal)) {
+ if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
return 0;
}
- acquireConnection(sql, connectionFlags, cancelationSignal); // might throw
+ acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
try {
return mConnection.executeForChangedRowCount(sql, bindArgs,
- cancelationSignal); // might throw
+ cancellationSignal); // might throw
} finally {
releaseConnection(); // might throw
}
@@ -766,7 +766,7 @@
* @param bindArgs The arguments to bind, or null if none.
* @param connectionFlags The connection flags to use if a connection must be
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
- * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
* @return The row id of the last row that was inserted, or 0 if none.
*
* @throws SQLiteException if an error occurs, such as a syntax error
@@ -774,19 +774,19 @@
* @throws OperationCanceledException if the operation was canceled.
*/
public long executeForLastInsertedRowId(String sql, Object[] bindArgs, int connectionFlags,
- CancelationSignal cancelationSignal) {
+ CancellationSignal cancellationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
- if (executeSpecial(sql, bindArgs, connectionFlags, cancelationSignal)) {
+ if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
return 0;
}
- acquireConnection(sql, connectionFlags, cancelationSignal); // might throw
+ acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
try {
return mConnection.executeForLastInsertedRowId(sql, bindArgs,
- cancelationSignal); // might throw
+ cancellationSignal); // might throw
} finally {
releaseConnection(); // might throw
}
@@ -808,7 +808,7 @@
* regagless of whether they fit in the window.
* @param connectionFlags The connection flags to use if a connection must be
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
- * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
* @return The number of rows that were counted during query execution. Might
* not be all rows in the result set unless <code>countAllRows</code> is true.
*
@@ -818,7 +818,7 @@
*/
public int executeForCursorWindow(String sql, Object[] bindArgs,
CursorWindow window, int startPos, int requiredPos, boolean countAllRows,
- int connectionFlags, CancelationSignal cancelationSignal) {
+ int connectionFlags, CancellationSignal cancellationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
@@ -826,16 +826,16 @@
throw new IllegalArgumentException("window must not be null.");
}
- if (executeSpecial(sql, bindArgs, connectionFlags, cancelationSignal)) {
+ if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
window.clear();
return 0;
}
- acquireConnection(sql, connectionFlags, cancelationSignal); // might throw
+ acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
try {
return mConnection.executeForCursorWindow(sql, bindArgs,
window, startPos, requiredPos, countAllRows,
- cancelationSignal); // might throw
+ cancellationSignal); // might throw
} finally {
releaseConnection(); // might throw
}
@@ -854,7 +854,7 @@
* @param bindArgs The arguments to bind, or null if none.
* @param connectionFlags The connection flags to use if a connection must be
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
- * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
* @return True if the statement was of a special form that was handled here,
* false otherwise.
*
@@ -863,36 +863,36 @@
* @throws OperationCanceledException if the operation was canceled.
*/
private boolean executeSpecial(String sql, Object[] bindArgs, int connectionFlags,
- CancelationSignal cancelationSignal) {
- if (cancelationSignal != null) {
- cancelationSignal.throwIfCanceled();
+ CancellationSignal cancellationSignal) {
+ if (cancellationSignal != null) {
+ cancellationSignal.throwIfCanceled();
}
final int type = DatabaseUtils.getSqlStatementType(sql);
switch (type) {
case DatabaseUtils.STATEMENT_BEGIN:
beginTransaction(TRANSACTION_MODE_EXCLUSIVE, null, connectionFlags,
- cancelationSignal);
+ cancellationSignal);
return true;
case DatabaseUtils.STATEMENT_COMMIT:
setTransactionSuccessful();
- endTransaction(cancelationSignal);
+ endTransaction(cancellationSignal);
return true;
case DatabaseUtils.STATEMENT_ABORT:
- endTransaction(cancelationSignal);
+ endTransaction(cancellationSignal);
return true;
}
return false;
}
private void acquireConnection(String sql, int connectionFlags,
- CancelationSignal cancelationSignal) {
+ CancellationSignal cancellationSignal) {
if (mConnection == null) {
assert mConnectionUseCount == 0;
mConnection = mConnectionPool.acquireConnection(sql, connectionFlags,
- cancelationSignal); // might throw
+ cancellationSignal); // might throw
mConnectionFlags = connectionFlags;
}
mConnectionUseCount += 1;
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index a569317..2eef8f4 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -142,8 +142,19 @@
* If an application uses the network in the background, it should listen
* for this broadcast and stop using the background data if the value is
* {@code false}.
+ * <p>
+ *
+ * @deprecated As of {@link VERSION_CODES#ICE_CREAM_SANDWICH}, availability
+ * of background data depends on several combined factors, and
+ * this broadcast is no longer sent. Instead, when background
+ * data is unavailable, {@link #getActiveNetworkInfo()} will now
+ * appear disconnected. During first boot after a platform
+ * upgrade, this broadcast will be sent once if
+ * {@link #getBackgroundDataSetting()} was {@code false} before
+ * the upgrade.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @Deprecated
public static final String ACTION_BACKGROUND_DATA_SETTING_CHANGED =
"android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 633c38e0..442535a 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -43,7 +43,7 @@
NetworkPolicy[] getNetworkPolicies();
/** Snooze limit on policy matching given template. */
- void snoozePolicy(in NetworkTemplate template);
+ void snoozeLimit(in NetworkTemplate template);
/** Control if background data is restricted system-wide. */
void setRestrictBackground(boolean restrictBackground);
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index d9ea700..04cf1a3 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -38,18 +38,25 @@
public int cycleDay;
public long warningBytes;
public long limitBytes;
- public long lastSnooze;
+ public long lastWarningSnooze;
+ public long lastLimitSnooze;
public boolean metered;
private static final long DEFAULT_MTU = 1500;
- public NetworkPolicy(NetworkTemplate template, int cycleDay, long warningBytes, long limitBytes,
- long lastSnooze, boolean metered) {
+ public NetworkPolicy(NetworkTemplate template, int cycleDay, long warningBytes,
+ long limitBytes, boolean metered) {
+ this(template, cycleDay, warningBytes, limitBytes, SNOOZE_NEVER, SNOOZE_NEVER, metered);
+ }
+
+ public NetworkPolicy(NetworkTemplate template, int cycleDay, long warningBytes,
+ long limitBytes, long lastWarningSnooze, long lastLimitSnooze, boolean metered) {
this.template = checkNotNull(template, "missing NetworkTemplate");
this.cycleDay = cycleDay;
this.warningBytes = warningBytes;
this.limitBytes = limitBytes;
- this.lastSnooze = lastSnooze;
+ this.lastWarningSnooze = lastWarningSnooze;
+ this.lastLimitSnooze = lastLimitSnooze;
this.metered = metered;
}
@@ -58,7 +65,8 @@
cycleDay = in.readInt();
warningBytes = in.readLong();
limitBytes = in.readLong();
- lastSnooze = in.readLong();
+ lastWarningSnooze = in.readLong();
+ lastLimitSnooze = in.readLong();
metered = in.readInt() != 0;
}
@@ -68,7 +76,8 @@
dest.writeInt(cycleDay);
dest.writeLong(warningBytes);
dest.writeLong(limitBytes);
- dest.writeLong(lastSnooze);
+ dest.writeLong(lastWarningSnooze);
+ dest.writeLong(lastLimitSnooze);
dest.writeInt(metered ? 1 : 0);
}
@@ -78,6 +87,13 @@
}
/**
+ * Test if given measurement is over {@link #warningBytes}.
+ */
+ public boolean isOverWarning(long totalBytes) {
+ return warningBytes != WARNING_DISABLED && totalBytes >= warningBytes;
+ }
+
+ /**
* Test if given measurement is near enough to {@link #limitBytes} to be
* considered over-limit.
*/
@@ -88,6 +104,14 @@
return limitBytes != LIMIT_DISABLED && totalBytes >= limitBytes;
}
+ /**
+ * Clear any existing snooze values, setting to {@link #SNOOZE_NEVER}.
+ */
+ public void clearSnooze() {
+ lastWarningSnooze = SNOOZE_NEVER;
+ lastLimitSnooze = SNOOZE_NEVER;
+ }
+
/** {@inheritDoc} */
public int compareTo(NetworkPolicy another) {
if (another == null || another.limitBytes == LIMIT_DISABLED) {
@@ -103,7 +127,8 @@
@Override
public int hashCode() {
- return Objects.hashCode(template, cycleDay, warningBytes, limitBytes, lastSnooze, metered);
+ return Objects.hashCode(template, cycleDay, warningBytes, limitBytes, lastWarningSnooze,
+ lastLimitSnooze, metered);
}
@Override
@@ -111,8 +136,10 @@
if (obj instanceof NetworkPolicy) {
final NetworkPolicy other = (NetworkPolicy) obj;
return cycleDay == other.cycleDay && warningBytes == other.warningBytes
- && limitBytes == other.limitBytes && lastSnooze == other.lastSnooze
- && metered == other.metered && Objects.equal(template, other.template);
+ && limitBytes == other.limitBytes
+ && lastWarningSnooze == other.lastWarningSnooze
+ && lastLimitSnooze == other.lastLimitSnooze && metered == other.metered
+ && Objects.equal(template, other.template);
}
return false;
}
@@ -120,8 +147,9 @@
@Override
public String toString() {
return "NetworkPolicy[" + template + "]: cycleDay=" + cycleDay + ", warningBytes="
- + warningBytes + ", limitBytes=" + limitBytes + ", lastSnooze=" + lastSnooze
- + ", metered=" + metered;
+ + warningBytes + ", limitBytes=" + limitBytes + ", lastWarningSnooze="
+ + lastWarningSnooze + ", lastLimitSnooze=" + lastLimitSnooze + ", metered="
+ + metered;
}
public static final Creator<NetworkPolicy> CREATOR = new Creator<NetworkPolicy>() {
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index dfdea38..973fac1 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -44,6 +44,13 @@
*/
public final static int UNSUPPORTED = -1;
+ /** @hide */
+ public static final long KB_IN_BYTES = 1024;
+ /** @hide */
+ public static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
+ /** @hide */
+ public static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
+
/**
* Special UID value used when collecting {@link NetworkStatsHistory} for
* removed applications.
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 24569fa..577fc43 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -49,6 +49,7 @@
private static final boolean FIND_POTENTIAL_LEAKS = false;
private static final String TAG = "Binder";
+ /* mObject is used by native code, do not remove or rename */
private int mObject;
private IInterface mOwner;
private String mDescriptor;
@@ -70,7 +71,35 @@
* incoming transaction, then its own uid is returned.
*/
public static final native int getCallingUid();
-
+
+ /**
+ * Return the original ID of the user assigned to the process that sent you the current
+ * transaction that is being processed. This uid can be used with higher-level system services
+ * to determine its identity and check permissions. If the current thread is not currently
+ * executing an incoming transaction, then its own uid is returned.
+ * <p/>
+ * This value cannot be reset by calls to {@link #clearCallingIdentity()}.
+ * @hide
+ */
+ public static final int getOrigCallingUid() {
+ if (UserId.MU_ENABLED) {
+ return getOrigCallingUidNative();
+ } else {
+ return getCallingUid();
+ }
+ }
+
+ private static final native int getOrigCallingUidNative();
+
+ /**
+ * Utility function to return the user id of the calling process.
+ * @return userId of the calling process, extracted from the callingUid
+ * @hide
+ */
+ public static final int getOrigCallingUser() {
+ return UserId.getUserId(getOrigCallingUid());
+ }
+
/**
* Reset the identity of the incoming IPC on the current thread. This can
* be useful if, while handling an incoming call, you will be calling
diff --git a/core/java/android/os/UserId.java b/core/java/android/os/UserId.java
new file mode 100644
index 0000000..4124d51
--- /dev/null
+++ b/core/java/android/os/UserId.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * @hide
+ */
+public final class UserId {
+ /**
+ * Range of IDs allocated for a user.
+ *
+ * @hide
+ */
+ public static final int PER_USER_RANGE = 100000;
+
+ public static final int USER_ALL = -1;
+
+ /**
+ * Enable multi-user related side effects. Set this to false if there are problems with single
+ * user usecases.
+ * */
+ public static final boolean MU_ENABLED = true;
+
+ /**
+ * Checks to see if the user id is the same for the two uids, i.e., they belong to the same
+ * user.
+ * @hide
+ */
+ public static final boolean isSameUser(int uid1, int uid2) {
+ return getUserId(uid1) == getUserId(uid2);
+ }
+
+ /**
+ * Checks to see if both uids are referring to the same app id, ignoring the user id part of the
+ * uids.
+ * @param uid1 uid to compare
+ * @param uid2 other uid to compare
+ * @return whether the appId is the same for both uids
+ * @hide
+ */
+ public static final boolean isSameApp(int uid1, int uid2) {
+ return getAppId(uid1) == getAppId(uid2);
+ }
+
+ /**
+ * Returns the user id for a given uid.
+ * @hide
+ */
+ public static final int getUserId(int uid) {
+ if (MU_ENABLED) {
+ return uid / PER_USER_RANGE;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Returns the uid that is composed from the userId and the appId.
+ * @hide
+ */
+ public static final int getUid(int userId, int appId) {
+ if (MU_ENABLED) {
+ return userId * PER_USER_RANGE + (appId % PER_USER_RANGE);
+ } else {
+ return appId;
+ }
+ }
+
+ /**
+ * Returns the app id (or base uid) for a given uid, stripping out the user id from it.
+ * @hide
+ */
+ public static final int getAppId(int uid) {
+ return uid % PER_USER_RANGE;
+ }
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f14d27e..375e5e4 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1678,6 +1678,13 @@
public static final String TRANSITION_ANIMATION_SCALE = "transition_animation_scale";
/**
+ * Scaling factor for Animator-based animations. This affects both the start delay and
+ * duration of all such animations. Setting to 0 will cause animations to end immediately.
+ * The default value is 1.
+ */
+ public static final String ANIMATOR_DURATION_SCALE = "animator_duration_scale";
+
+ /**
* Scaling factor for normal window animations. Setting to 0 will disable window
* animations.
* @hide
@@ -2475,6 +2482,11 @@
Uri.parse("content://" + AUTHORITY + "/secure");
/**
+ * Whether user has enabled development settings.
+ */
+ public static final String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
+
+ /**
* Whether ADB is enabled.
*/
public static final String ADB_ENABLED = "adb_enabled";
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 63de128..3ee275c 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -26,7 +26,11 @@
import android.util.Log;
/**
- * Coodinates animations and drawing for UI on a particular thread.
+ * Coordinates animations and drawing for UI on a particular thread.
+ *
+ * This object is thread-safe. Other threads can add and remove listeners
+ * or schedule work to occur at a later time on the UI thread.
+ *
* @hide
*/
public final class Choreographer extends Handler {
@@ -44,7 +48,7 @@
private static final long DEFAULT_FRAME_DELAY = 10;
// The number of milliseconds between animation frames.
- private static long sFrameDelay = DEFAULT_FRAME_DELAY;
+ private static volatile long sFrameDelay = DEFAULT_FRAME_DELAY;
// Thread local storage for the choreographer.
private static final ThreadLocal<Choreographer> sThreadInstance =
@@ -75,6 +79,8 @@
private static final int MSG_DO_ANIMATION = 0;
private static final int MSG_DO_DRAW = 1;
+ private final Object mLock = new Object();
+
private final Looper mLooper;
private OnAnimateListener[] mOnAnimateListeners;
@@ -94,8 +100,8 @@
}
/**
- * Gets the choreographer for this thread.
- * Must be called on the UI thread.
+ * Gets the choreographer for the calling thread. Must be called from
+ * a thread that already has a {@link android.os.Looper} associated with it.
*
* @return The choreographer for this thread.
* @throws IllegalStateException if the thread does not have a looper.
@@ -138,9 +144,14 @@
/**
* Schedules animation (and drawing) to occur on the next frame synchronization boundary.
- * Must be called on the UI thread.
*/
public void scheduleAnimation() {
+ synchronized (mLock) {
+ scheduleAnimationLocked();
+ }
+ }
+
+ private void scheduleAnimationLocked() {
if (!mAnimationScheduled) {
mAnimationScheduled = true;
if (USE_VSYNC) {
@@ -163,23 +174,47 @@
}
/**
+ * Returns true if {@link #scheduleAnimation()} has been called but
+ * {@link OnAnimateListener#onAnimate() OnAnimateListener.onAnimate()} has
+ * not yet been called.
+ */
+ public boolean isAnimationScheduled() {
+ synchronized (mLock) {
+ return mAnimationScheduled;
+ }
+ }
+
+ /**
* Schedules drawing to occur on the next frame synchronization boundary.
* Must be called on the UI thread.
*/
public void scheduleDraw() {
- if (!mDrawScheduled) {
- mDrawScheduled = true;
- if (USE_ANIMATION_TIMER_FOR_DRAW) {
- scheduleAnimation();
- } else {
- if (DEBUG) {
- Log.d(TAG, "Scheduling draw immediately.");
+ synchronized (mLock) {
+ if (!mDrawScheduled) {
+ mDrawScheduled = true;
+ if (USE_ANIMATION_TIMER_FOR_DRAW) {
+ scheduleAnimationLocked();
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "Scheduling draw immediately.");
+ }
+ sendEmptyMessage(MSG_DO_DRAW);
}
- sendEmptyMessage(MSG_DO_DRAW);
}
}
}
+ /**
+ * Returns true if {@link #scheduleDraw()} has been called but
+ * {@link OnDrawListener#onDraw() OnDrawListener.onDraw()} has
+ * not yet been called.
+ */
+ public boolean isDrawScheduled() {
+ synchronized (mLock) {
+ return mDrawScheduled;
+ }
+ }
+
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -193,60 +228,75 @@
}
private void doAnimation() {
- if (mAnimationScheduled) {
- mAnimationScheduled = false;
-
- final long start = SystemClock.uptimeMillis();
- if (DEBUG) {
- Log.d(TAG, "Performing animation: " + Math.max(0, start - mLastAnimationTime)
- + " ms have elapsed since previous animation.");
- }
- mLastAnimationTime = start;
-
- final OnAnimateListener[] listeners = mOnAnimateListeners;
- if (listeners != null) {
- for (int i = 0; i < listeners.length; i++) {
- listeners[i].onAnimate();
- }
- }
-
- if (DEBUG) {
- Log.d(TAG, "Animation took " + (SystemClock.uptimeMillis() - start) + " ms.");
- }
- }
+ doAnimationInner();
if (USE_ANIMATION_TIMER_FOR_DRAW) {
doDraw();
}
}
+ private void doAnimationInner() {
+ final long start;
+ final OnAnimateListener[] listeners;
+ synchronized (mLock) {
+ if (!mAnimationScheduled) {
+ return; // no work to do
+ }
+ mAnimationScheduled = false;
+
+ start = SystemClock.uptimeMillis();
+ if (DEBUG) {
+ Log.d(TAG, "Performing animation: " + Math.max(0, start - mLastAnimationTime)
+ + " ms have elapsed since previous animation.");
+ }
+ mLastAnimationTime = start;
+
+ listeners = mOnAnimateListeners;
+ }
+
+ if (listeners != null) {
+ for (int i = 0; i < listeners.length; i++) {
+ listeners[i].onAnimate();
+ }
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "Animation took " + (SystemClock.uptimeMillis() - start) + " ms.");
+ }
+ }
+
private void doDraw() {
- if (mDrawScheduled) {
+ final long start;
+ final OnDrawListener[] listeners;
+ synchronized (mLock) {
+ if (!mDrawScheduled) {
+ return; // no work to do
+ }
mDrawScheduled = false;
- final long start = SystemClock.uptimeMillis();
+ start = SystemClock.uptimeMillis();
if (DEBUG) {
Log.d(TAG, "Performing draw: " + Math.max(0, start - mLastDrawTime)
+ " ms have elapsed since previous draw.");
}
mLastDrawTime = start;
- final OnDrawListener[] listeners = mOnDrawListeners;
- if (listeners != null) {
- for (int i = 0; i < listeners.length; i++) {
- listeners[i].onDraw();
- }
- }
+ listeners = mOnDrawListeners;
+ }
- if (DEBUG) {
- Log.d(TAG, "Draw took " + (SystemClock.uptimeMillis() - start) + " ms.");
+ if (listeners != null) {
+ for (int i = 0; i < listeners.length; i++) {
+ listeners[i].onDraw();
}
}
+
+ if (DEBUG) {
+ Log.d(TAG, "Draw took " + (SystemClock.uptimeMillis() - start) + " ms.");
+ }
}
/**
* Adds an animation listener.
- * Must be called on the UI thread.
*
* @param listener The listener to add.
*/
@@ -259,13 +309,14 @@
Log.d(TAG, "Adding onAnimate listener: " + listener);
}
- mOnAnimateListeners = ArrayUtils.appendElement(OnAnimateListener.class,
- mOnAnimateListeners, listener);
+ synchronized (mLock) {
+ mOnAnimateListeners = ArrayUtils.appendElement(OnAnimateListener.class,
+ mOnAnimateListeners, listener);
+ }
}
/**
* Removes an animation listener.
- * Must be called on the UI thread.
*
* @param listener The listener to remove.
*/
@@ -278,14 +329,15 @@
Log.d(TAG, "Removing onAnimate listener: " + listener);
}
- mOnAnimateListeners = ArrayUtils.removeElement(OnAnimateListener.class,
- mOnAnimateListeners, listener);
- stopTimingLoopIfNoListeners();
+ synchronized (mLock) {
+ mOnAnimateListeners = ArrayUtils.removeElement(OnAnimateListener.class,
+ mOnAnimateListeners, listener);
+ stopTimingLoopIfNoListenersLocked();
+ }
}
/**
* Adds a draw listener.
- * Must be called on the UI thread.
*
* @param listener The listener to add.
*/
@@ -298,8 +350,10 @@
Log.d(TAG, "Adding onDraw listener: " + listener);
}
- mOnDrawListeners = ArrayUtils.appendElement(OnDrawListener.class,
- mOnDrawListeners, listener);
+ synchronized (mLock) {
+ mOnDrawListeners = ArrayUtils.appendElement(OnDrawListener.class,
+ mOnDrawListeners, listener);
+ }
}
/**
@@ -317,12 +371,14 @@
Log.d(TAG, "Removing onDraw listener: " + listener);
}
- mOnDrawListeners = ArrayUtils.removeElement(OnDrawListener.class,
- mOnDrawListeners, listener);
- stopTimingLoopIfNoListeners();
+ synchronized (mLock) {
+ mOnDrawListeners = ArrayUtils.removeElement(OnDrawListener.class,
+ mOnDrawListeners, listener);
+ stopTimingLoopIfNoListenersLocked();
+ }
}
- private void stopTimingLoopIfNoListeners() {
+ private void stopTimingLoopIfNoListenersLocked() {
if (mOnDrawListeners == null && mOnAnimateListeners == null) {
if (DEBUG) {
Log.d(TAG, "Stopping timing loop.");
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d80d080..8d32edc 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2166,15 +2166,13 @@
float mScaleY = 1f;
/**
- * The amount of scale in the x direction around the pivot point. A
- * value of 1 means no scaling is applied.
+ * The x location of the point around which the view is rotated and scaled.
*/
@ViewDebug.ExportedProperty
float mPivotX = 0f;
/**
- * The amount of scale in the y direction around the pivot point. A
- * value of 1 means no scaling is applied.
+ * The y location of the point around which the view is rotated and scaled.
*/
@ViewDebug.ExportedProperty
float mPivotY = 0f;
@@ -3761,8 +3759,14 @@
}
/**
- * Called when this view wants to give up focus. This will cause
- * {@link #onFocusChanged(boolean, int, android.graphics.Rect)} to be called.
+ * Called when this view wants to give up focus. If focus is cleared
+ * {@link #onFocusChanged(boolean, int, android.graphics.Rect)} is called.
+ * <p>
+ * <strong>Note:</strong> When a View clears focus the framework is trying
+ * to give focus to the first focusable View from the top. Hence, if this
+ * View is the first from the top that can take focus, then its focus will
+ * not be cleared nor will the focus change callback be invoked.
+ * </p>
*/
public void clearFocus() {
if (DBG) {
@@ -7306,6 +7310,7 @@
*
* @return The degrees of rotation.
*/
+ @ViewDebug.ExportedProperty(category = "drawing")
public float getRotation() {
return mTransformationInfo != null ? mTransformationInfo.mRotation : 0;
}
@@ -7347,6 +7352,7 @@
*
* @return The degrees of Y rotation.
*/
+ @ViewDebug.ExportedProperty(category = "drawing")
public float getRotationY() {
return mTransformationInfo != null ? mTransformationInfo.mRotationY : 0;
}
@@ -7393,6 +7399,7 @@
*
* @return The degrees of X rotation.
*/
+ @ViewDebug.ExportedProperty(category = "drawing")
public float getRotationX() {
return mTransformationInfo != null ? mTransformationInfo.mRotationX : 0;
}
@@ -7440,6 +7447,7 @@
* @see #getPivotY()
* @return The scaling factor.
*/
+ @ViewDebug.ExportedProperty(category = "drawing")
public float getScaleX() {
return mTransformationInfo != null ? mTransformationInfo.mScaleX : 1;
}
@@ -7478,6 +7486,7 @@
* @see #getPivotY()
* @return The scaling factor.
*/
+ @ViewDebug.ExportedProperty(category = "drawing")
public float getScaleY() {
return mTransformationInfo != null ? mTransformationInfo.mScaleY : 1;
}
@@ -7516,6 +7525,7 @@
* @see #getPivotY()
* @return The x location of the pivot point.
*/
+ @ViewDebug.ExportedProperty(category = "drawing")
public float getPivotX() {
return mTransformationInfo != null ? mTransformationInfo.mPivotX : 0;
}
@@ -7560,6 +7570,7 @@
* @see #getPivotY()
* @return The y location of the pivot point.
*/
+ @ViewDebug.ExportedProperty(category = "drawing")
public float getPivotY() {
return mTransformationInfo != null ? mTransformationInfo.mPivotY : 0;
}
@@ -7600,6 +7611,7 @@
* <p>By default this is 1.0f.
* @return The opacity of the view.
*/
+ @ViewDebug.ExportedProperty(category = "drawing")
public float getAlpha() {
return mTransformationInfo != null ? mTransformationInfo.mAlpha : 1;
}
@@ -7613,6 +7625,10 @@
* equivalent to calling {@link #setLayerType(int, android.graphics.Paint)} and
* setting a hardware layer.</p>
*
+ * <p>Note that setting alpha to a translucent value (0 < alpha < 1) may have
+ * performance implications. It is generally best to use the alpha property sparingly and
+ * transiently, as in the case of fading animations.</p>
+ *
* @param alpha The opacity of the view.
*
* @see #setLayerType(int, android.graphics.Paint)
@@ -7910,6 +7926,7 @@
*
* @return The visual x position of this view, in pixels.
*/
+ @ViewDebug.ExportedProperty(category = "drawing")
public float getX() {
return mLeft + (mTransformationInfo != null ? mTransformationInfo.mTranslationX : 0);
}
@@ -7932,6 +7949,7 @@
*
* @return The visual y position of this view, in pixels.
*/
+ @ViewDebug.ExportedProperty(category = "drawing")
public float getY() {
return mTop + (mTransformationInfo != null ? mTransformationInfo.mTranslationY : 0);
}
@@ -7955,6 +7973,7 @@
*
* @return The horizontal position of this view relative to its left position, in pixels.
*/
+ @ViewDebug.ExportedProperty(category = "drawing")
public float getTranslationX() {
return mTransformationInfo != null ? mTransformationInfo.mTranslationX : 0;
}
@@ -7991,6 +8010,7 @@
* @return The vertical position of this view relative to its top position,
* in pixels.
*/
+ @ViewDebug.ExportedProperty(category = "drawing")
public float getTranslationY() {
return mTransformationInfo != null ? mTransformationInfo.mTranslationY : 0;
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3f61e6b..cbf4b5a 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -18,11 +18,13 @@
import android.Manifest;
import android.animation.LayoutTransition;
+import android.animation.ValueAnimator;
import android.app.ActivityManagerNative;
import android.content.ClipDescription;
import android.content.ComponentCallbacks;
import android.content.ComponentCallbacks2;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -82,7 +84,6 @@
import java.io.IOException;
import java.io.OutputStream;
-import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -140,6 +141,10 @@
static final ArrayList<ComponentCallbacks> sConfigCallbacks
= new ArrayList<ComponentCallbacks>();
+ private static boolean sUseRenderThread = false;
+ private static boolean sRenderThreadQueried = false;
+ private static final Object[] sRenderThreadQueryLock = new Object[0];
+
long mLastTrackballTime = 0;
final TrackballAxis mTrackballAxisX = new TrackballAxis();
final TrackballAxis mTrackballAxisY = new TrackballAxis();
@@ -319,8 +324,11 @@
if (!mInitialized) {
try {
InputMethodManager imm = InputMethodManager.getInstance(mainLooper);
- sWindowSession = Display.getWindowManager().openSession(
+ IWindowManager windowManager = Display.getWindowManager();
+ sWindowSession = windowManager.openSession(
imm.getClient(), imm.getInputContext());
+ float animatorScale = windowManager.getAnimationScale(2);
+ ValueAnimator.setDurationScale(animatorScale);
mInitialized = true;
} catch (RemoteException e) {
}
@@ -381,6 +389,31 @@
mChoreographer = Choreographer.getInstance();
}
+ /**
+ * @return True if the application requests the use of a separate render thread,
+ * false otherwise
+ */
+ private static boolean isRenderThreadRequested(Context context) {
+ synchronized (sRenderThreadQueryLock) {
+ if (!sRenderThreadQueried) {
+ final PackageManager packageManager = context.getPackageManager();
+ final String packageName = context.getApplicationInfo().packageName;
+ try {
+ ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName,
+ PackageManager.GET_META_DATA);
+ if (applicationInfo.metaData != null) {
+ sUseRenderThread = applicationInfo.metaData.getBoolean(
+ "android.graphics.renderThread", false);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ } finally {
+ sRenderThreadQueried = true;
+ }
+ }
+ return sUseRenderThread;
+ }
+ }
+
public static void addFirstDrawHandler(Runnable callback) {
synchronized (sFirstDrawHandlers) {
if (!sFirstDrawComplete) {
@@ -451,7 +484,7 @@
// If the application owns the surface, don't enable hardware acceleration
if (mSurfaceHolder == null) {
- enableHardwareAcceleration(attrs);
+ enableHardwareAcceleration(mView.getContext(), attrs);
}
boolean restore = false;
@@ -611,7 +644,7 @@
}
}
- private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
+ private void enableHardwareAcceleration(Context context, WindowManager.LayoutParams attrs) {
mAttachInfo.mHardwareAccelerated = false;
mAttachInfo.mHardwareAccelerationRequested = false;
@@ -644,20 +677,27 @@
if (!HardwareRenderer.sRendererDisabled || (HardwareRenderer.sSystemRendererDisabled
&& forceHwAccelerated)) {
// Don't enable hardware acceleration when we're not on the main thread
- if (!HardwareRenderer.sSystemRendererDisabled
- && Looper.getMainLooper() != Looper.myLooper()) {
- Log.w(HardwareRenderer.LOG_TAG, "Attempting to initialize hardware "
+ if (!HardwareRenderer.sSystemRendererDisabled &&
+ Looper.getMainLooper() != Looper.myLooper()) {
+ Log.w(HardwareRenderer.LOG_TAG, "Attempting to initialize hardware "
+ "acceleration outside of the main thread, aborting");
return;
}
- final boolean translucent = attrs.format != PixelFormat.OPAQUE;
+ boolean renderThread = isRenderThreadRequested(context);
+ if (renderThread) {
+ Log.i(HardwareRenderer.LOG_TAG, "Render threat initiated");
+ }
+
if (mAttachInfo.mHardwareRenderer != null) {
mAttachInfo.mHardwareRenderer.destroy(true);
- }
+ }
+
+ final boolean translucent = attrs.format != PixelFormat.OPAQUE;
mAttachInfo.mHardwareRenderer = HardwareRenderer.createGlRenderer(2, translucent);
mAttachInfo.mHardwareAccelerated = mAttachInfo.mHardwareAccelerationRequested
= mAttachInfo.mHardwareRenderer != null;
+
} else if (fakeHwAccelerated) {
// The window had wanted to use hardware acceleration, but this
// is not allowed in its process. By setting this flag, it can
@@ -3444,11 +3484,11 @@
if (args.localChanges != 0) {
if (mAttachInfo != null) {
mAttachInfo.mSystemUiVisibility =
- (mAttachInfo.mSystemUiVisibility&~args.localChanges)
- | (args.localValue&args.localChanges);
+ (mAttachInfo.mSystemUiVisibility & ~args.localChanges) |
+ (args.localValue & args.localChanges);
+ mAttachInfo.mRecomputeGlobalAttributes = true;
}
mView.updateLocalSystemUiVisibility(args.localValue, args.localChanges);
- mAttachInfo.mRecomputeGlobalAttributes = true;
scheduleTraversals();
}
mView.dispatchSystemUiVisibilityChanged(args.globalVisibility);
@@ -3602,7 +3642,7 @@
mView.debug();
}
- public void dumpGfxInfo(PrintWriter pw, int[] info) {
+ public void dumpGfxInfo(int[] info) {
if (mView != null) {
getGfxInfo(mView, info);
} else {
@@ -3714,7 +3754,7 @@
* Represents a pending input event that is waiting in a queue.
*
* Input events are processed in serial order by the timestamp specified by
- * {@link InputEvent#getEventTime()}. In general, the input dispatcher delivers
+ * {@link InputEvent#getEventTimeNano()}. In general, the input dispatcher delivers
* one input event to the application at a time and waits for the application
* to finish handling it before delivering the next one.
*
@@ -3723,7 +3763,7 @@
* needing a queue on the application's side.
*/
private static final class QueuedInputEvent {
- public static final int FLAG_DELIVER_POST_IME = 1 << 0;
+ public static final int FLAG_DELIVER_POST_IME = 1;
public QueuedInputEvent mNext;
@@ -4842,7 +4882,7 @@
mPool.release(args);
List<AccessibilityNodeInfo> infos = null;
try {
- View target = null;
+ View target;
if (accessibilityViewId != View.NO_ID) {
target = findViewByAccessibilityId(accessibilityViewId);
} else {
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index d7113374..6bdc4e8 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -490,7 +490,7 @@
for (int i = 0; i < count; i++) {
ViewRootImpl root = mRoots[i];
- root.dumpGfxInfo(pw, info);
+ root.dumpGfxInfo(info);
String name = root.getClass().getName() + '@' +
Integer.toHexString(hashCode());
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 08bcc4d..3f5b45e 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -367,12 +367,52 @@
private class WebViewInputConnection extends BaseInputConnection {
// Used for mapping characters to keys typed.
private KeyCharacterMap mKeyCharacterMap;
+ private boolean mIsKeySentByMe;
public WebViewInputConnection() {
super(WebView.this, true);
}
@Override
+ public boolean sendKeyEvent(KeyEvent event) {
+ // Latin IME occasionally sends delete codes directly using
+ // sendKeyEvents. WebViewInputConnection should treat this
+ // as a deleteSurroundingText.
+ if (!mIsKeySentByMe
+ && event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
+ Editable editable = getEditable();
+ int selectionStart = Selection.getSelectionStart(editable);
+ int selectionEnd = Selection.getSelectionEnd(editable);
+ if (selectionEnd > 0 && (selectionStart == selectionEnd)) {
+ int action = event.getAction();
+ if (action == KeyEvent.ACTION_UP) {
+ return deleteSurroundingText(1, 0);
+ } else if (action == KeyEvent.ACTION_DOWN) {
+ return true; // the delete will happen in ACTION_UP
+ }
+ }
+ }
+ return super.sendKeyEvent(event);
+ }
+
+ public void setTextAndKeepSelection(CharSequence text) {
+ Editable editable = getEditable();
+ int selectionStart = Selection.getSelectionStart(editable);
+ int selectionEnd = Selection.getSelectionEnd(editable);
+ editable.replace(0, editable.length(), text);
+ InputMethodManager imm = InputMethodManager.peekInstance();
+ if (imm != null) {
+ // Since the text has changed, do not allow the IME to replace the
+ // existing text as though it were a completion.
+ imm.restartInput(WebView.this);
+ }
+ // Keep the previous selection.
+ selectionStart = Math.min(selectionStart, editable.length());
+ selectionEnd = Math.min(selectionEnd, editable.length());
+ setSelection(selectionStart, selectionEnd);
+ }
+
+ @Override
public boolean setComposingText(CharSequence text, int newCursorPosition) {
Editable editable = getEditable();
int start = getComposingSpanStart(editable);
@@ -393,7 +433,8 @@
@Override
public boolean commitText(CharSequence text, int newCursorPosition) {
setComposingText(text, newCursorPosition);
- finishComposingText();
+ int cursorPosition = Selection.getSelectionEnd(getEditable());
+ setComposingRegion(cursorPosition, cursorPosition);
return true;
}
@@ -417,6 +458,7 @@
* @param text The new text to replace the changed text.
*/
private void setNewText(int start, int end, CharSequence text) {
+ mIsKeySentByMe = true;
Editable editable = getEditable();
CharSequence original = editable.subSequence(start, end);
boolean isCharacterAdd = false;
@@ -434,10 +476,8 @@
}
if (isCharacterAdd) {
sendCharacter(text.charAt(textLength - 1));
- mTextGeneration++;
} else if (isCharacterDelete) {
sendDeleteKey();
- mTextGeneration++;
} else if (textLength != originalLength ||
!TextUtils.regionMatches(text, 0, original, 0,
textLength)) {
@@ -447,6 +487,7 @@
REPLACE_TEXT, start, end, text.toString());
mPrivateHandler.sendMessage(replaceMessage);
}
+ mIsKeySentByMe = false;
}
/**
@@ -509,7 +550,7 @@
private final RectF mVisibleContentRect = new RectF();
private boolean mGLViewportEmpty = false;
WebViewInputConnection mInputConnection = null;
-
+ private int mFieldPointer;
/**
* Transportation object for returning WebView across thread boundaries.
@@ -803,6 +844,7 @@
static final int HANDLE_ID_EXTENT = 3;
static boolean sDisableNavcache = false;
+ static boolean sEnableWebTextView = false;
// the color used to highlight the touch rectangles
static final int HIGHLIGHT_COLOR = 0x6633b5e5;
// the region indicating where the user touched on the screen
@@ -1424,7 +1466,6 @@
private void init() {
OnTrimMemoryListener.init(getContext());
sDisableNavcache = nativeDisableNavcache();
-
setWillNotDraw(false);
setFocusable(true);
setFocusableInTouchMode(true);
@@ -5101,6 +5142,9 @@
* multiline, and what text it contains. It also removes it if necessary.
*/
/* package */ void rebuildWebTextView() {
+ if (!sEnableWebTextView) {
+ return; // always use WebKit's text entry
+ }
// If the WebView does not have focus, do nothing until it gains focus.
if (!hasFocus() && (null == mWebTextView || !mWebTextView.hasFocus())) {
return;
@@ -8663,14 +8707,17 @@
case UPDATE_TEXTFIELD_TEXT_MSG_ID:
// Make sure that the textfield is currently focused
// and representing the same node as the pointer.
- if (inEditingMode() &&
- mWebTextView.isSameTextField(msg.arg1)) {
- if (msg.arg2 == mTextGeneration) {
- String text = (String) msg.obj;
- if (null == text) {
- text = "";
- }
+ if (msg.arg2 == mTextGeneration) {
+ String text = (String) msg.obj;
+ if (null == text) {
+ text = "";
+ }
+ if (inEditingMode() &&
+ mWebTextView.isSameTextField(msg.arg1)) {
mWebTextView.setTextAndKeepSelection(text);
+ } else if (mInputConnection != null &&
+ mFieldPointer == msg.arg1) {
+ mInputConnection.setTextAndKeepSelection(text);
}
}
break;
@@ -8961,15 +9008,8 @@
case INIT_EDIT_FIELD:
if (mInputConnection != null) {
mTextGeneration = 0;
- String text = (String)msg.obj;
- mInputConnection.beginBatchEdit();
- Editable editable = mInputConnection.getEditable();
- editable.replace(0, editable.length(), text);
- int start = msg.arg1;
- int end = msg.arg2;
- mInputConnection.setComposingRegion(end, end);
- mInputConnection.setSelection(start, end);
- mInputConnection.endBatchEdit();
+ mFieldPointer = msg.arg1;
+ mInputConnection.setTextAndKeepSelection((String) msg.obj);
}
break;
@@ -9109,7 +9149,7 @@
if (inEditingMode()
&& mWebTextView.isSameTextField(nodePointer)) {
mWebTextView.setSelectionFromWebKit(data.mStart, data.mEnd);
- } else if (mInputConnection != null){
+ } else if (mInputConnection != null && mFieldPointer == nodePointer) {
mInputConnection.setSelection(data.mStart, data.mEnd);
}
}
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 36b313c..ebf3e21 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -2775,12 +2775,16 @@
}
// called by JNI
- private void initEditField(String text, int start, int end) {
+ private void initEditField(int pointer, String text, int start, int end) {
if (mWebView == null) {
return;
}
Message.obtain(mWebView.mPrivateHandler,
- WebView.INIT_EDIT_FIELD, start, end, text).sendToTarget();
+ WebView.INIT_EDIT_FIELD, pointer, 0, text).sendToTarget();
+ Message.obtain(mWebView.mPrivateHandler,
+ WebView.REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID, pointer,
+ 0, new TextSelectionData(start, end, 0))
+ .sendToTarget();
}
private native void nativeUpdateFrameCacheIfLoading(int nativeClass);
@@ -2818,17 +2822,6 @@
}
// called by JNI
- private void requestKeyboardWithSelection(int pointer, int selStart,
- int selEnd, int textGeneration) {
- if (mWebView != null) {
- Message.obtain(mWebView.mPrivateHandler,
- WebView.REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID, pointer,
- textGeneration, new TextSelectionData(selStart, selEnd, 0))
- .sendToTarget();
- }
- }
-
- // called by JNI
private void requestKeyboard(boolean showKeyboard) {
if (mWebView != null) {
Message.obtain(mWebView.mPrivateHandler,
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index e20d12a..67fd059 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -1651,6 +1651,7 @@
// are focusable
if (mItemsCanFocus && hasFocus() && !sel.hasFocus()) {
final boolean focusWasTaken = (sel == focusLayoutRestoreDirectChild &&
+ focusLayoutRestoreView != null &&
focusLayoutRestoreView.requestFocus()) || sel.requestFocus();
if (!focusWasTaken) {
// selected item didn't take focus, fine, but still want
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 1592061..62afd61 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -427,13 +427,22 @@
public SetOnClickPendingIntent(Parcel parcel) {
viewId = parcel.readInt();
- pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
+
+ // We check a flag to determine if the parcel contains a PendingIntent.
+ if (parcel.readInt() != 0) {
+ pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
+ }
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(TAG);
dest.writeInt(viewId);
- pendingIntent.writeToParcel(dest, 0 /* no flags */);
+
+ // We use a flag to indicate whether the parcel contains a valid object.
+ dest.writeInt(pendingIntent != null ? 1 : 0);
+ if (pendingIntent != null) {
+ pendingIntent.writeToParcel(dest, 0 /* no flags */);
+ }
}
@Override
@@ -445,35 +454,39 @@
// sense, do they mean to set a PendingIntent template for the AdapterView's children?
if (mIsWidgetCollectionChild) {
Log.e("RemoteViews", "Cannot setOnClickPendingIntent for collection item " +
- "(id: " + viewId + ")");
+ "(id: " + viewId + ")");
// TODO: return; We'll let this slide until apps are up to date.
}
- if (target != null && pendingIntent != null) {
- OnClickListener listener = new OnClickListener() {
- public void onClick(View v) {
- // Find target view location in screen coordinates and
- // fill into PendingIntent before sending.
- final float appScale = v.getContext().getResources()
- .getCompatibilityInfo().applicationScale;
- final int[] pos = new int[2];
- v.getLocationOnScreen(pos);
-
- final Rect rect = new Rect();
- rect.left = (int) (pos[0] * appScale + 0.5f);
- rect.top = (int) (pos[1] * appScale + 0.5f);
- rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
- rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
-
- final Intent intent = new Intent();
- intent.setSourceBounds(rect);
- startIntentSafely(v.getContext(), pendingIntent, intent);
- }
- };
+ if (target != null) {
+ // If the pendingIntent is null, we clear the onClickListener
+ OnClickListener listener = null;
+ if (pendingIntent != null) {
+ listener = new OnClickListener() {
+ public void onClick(View v) {
+ // Find target view location in screen coordinates and
+ // fill into PendingIntent before sending.
+ final float appScale = v.getContext().getResources()
+ .getCompatibilityInfo().applicationScale;
+ final int[] pos = new int[2];
+ v.getLocationOnScreen(pos);
+
+ final Rect rect = new Rect();
+ rect.left = (int) (pos[0] * appScale + 0.5f);
+ rect.top = (int) (pos[1] * appScale + 0.5f);
+ rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
+ rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
+
+ final Intent intent = new Intent();
+ intent.setSourceBounds(rect);
+ startIntentSafely(v.getContext(), pendingIntent, intent);
+ }
+ };
+ }
target.setOnClickListener(listener);
}
}
-
+
int viewId;
PendingIntent pendingIntent;
@@ -667,6 +680,9 @@
Log.d("RemoteViews", "read viewId=0x" + Integer.toHexString(this.viewId)
+ " methodName=" + this.methodName + " type=" + this.type);
}
+
+ // For some values that may have been null, we first check a flag to see if they were
+ // written to the parcel.
switch (this.type) {
case BOOLEAN:
this.value = in.readInt() != 0;
@@ -699,16 +715,22 @@
this.value = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
break;
case URI:
- this.value = Uri.CREATOR.createFromParcel(in);
+ if (in.readInt() != 0) {
+ this.value = Uri.CREATOR.createFromParcel(in);
+ }
break;
case BITMAP:
- this.value = Bitmap.CREATOR.createFromParcel(in);
+ if (in.readInt() != 0) {
+ this.value = Bitmap.CREATOR.createFromParcel(in);
+ }
break;
case BUNDLE:
this.value = in.readBundle();
break;
case INTENT:
- this.value = Intent.CREATOR.createFromParcel(in);
+ if (in.readInt() != 0) {
+ this.value = Intent.CREATOR.createFromParcel(in);
+ }
break;
default:
break;
@@ -725,6 +747,9 @@
Log.d("RemoteViews", "write viewId=0x" + Integer.toHexString(this.viewId)
+ " methodName=" + this.methodName + " type=" + this.type);
}
+
+ // For some values which are null, we record an integer flag to indicate whether
+ // we have written a valid value to the parcel.
switch (this.type) {
case BOOLEAN:
out.writeInt((Boolean) this.value ? 1 : 0);
@@ -757,16 +782,25 @@
TextUtils.writeToParcel((CharSequence)this.value, out, flags);
break;
case URI:
- ((Uri)this.value).writeToParcel(out, flags);
+ out.writeInt(this.value != null ? 1 : 0);
+ if (this.value != null) {
+ ((Uri)this.value).writeToParcel(out, flags);
+ }
break;
case BITMAP:
- ((Bitmap)this.value).writeToParcel(out, flags);
+ out.writeInt(this.value != null ? 1 : 0);
+ if (this.value != null) {
+ ((Bitmap)this.value).writeToParcel(out, flags);
+ }
break;
case BUNDLE:
out.writeBundle((Bundle) this.value);
break;
case INTENT:
- ((Intent)this.value).writeToParcel(out, flags);
+ out.writeInt(this.value != null ? 1 : 0);
+ if (this.value != null) {
+ ((Intent)this.value).writeToParcel(out, flags);
+ }
break;
default:
break;
diff --git a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
index ebd355a..d51ced11 100644
--- a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
+++ b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
@@ -653,6 +653,7 @@
case MotionEvent.ACTION_CANCEL:
handleMove(event);
+ handleCancel(event);
handled = true;
break;
}
@@ -678,6 +679,12 @@
if (DEBUG && mDragging) Log.v(TAG, "** Handle RELEASE");
switchToState(STATE_FINISH, event.getX(), event.getY());
}
+
+ private void handleCancel(MotionEvent event) {
+ if (DEBUG && mDragging) Log.v(TAG, "** Handle CANCEL");
+ mActiveTarget = -1; // Drop the active target if canceled.
+ switchToState(STATE_FINISH, event.getX(), event.getY());
+ }
private void handleMove(MotionEvent event) {
if (!mDragging) {
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index c797a73..7f5d54d 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -29,7 +29,10 @@
namespace android {
//--------------------------------------------------------------------------------------------------
-#define TYPEFACE_ARABIC "/system/fonts/DroidNaskh-Regular.ttf"
+// Using DroidSansArabic for shaping Arabic with Harfbuzz because its metrics are more compatible
+// with the "Roboto" metrics (compared to DroidNaskh-Regular). When we will have an Arabic font
+// whose metrics are similar to the Roboto ones, then we will need to use it for shaping.
+#define TYPEFACE_ARABIC "/system/fonts/DroidSansArabic.ttf"
#define TYPE_FACE_HEBREW_REGULAR "/system/fonts/DroidSansHebrew-Regular.ttf"
#define TYPE_FACE_HEBREW_BOLD "/system/fonts/DroidSansHebrew-Bold.ttf"
#define TYPEFACE_BENGALI "/system/fonts/Lohit-Bengali.ttf"
@@ -37,6 +40,8 @@
ANDROID_SINGLETON_STATIC_INSTANCE(TextLayoutEngine);
+static KeyedVector<UChar, UChar> gBidiMirrored;
+
//--------------------------------------------------------------------------------------------------
TextLayoutCache::TextLayoutCache(TextLayoutShaper* shaper) :
@@ -350,6 +355,23 @@
mShaperItem.font = &mFontRec;
mShaperItem.font->userData = &mShapingPaint;
+
+ // Fill the BiDi mirrored chars map
+ // See: http://www.unicode.org/Public/6.0.0/ucd/extracted/DerivedBinaryProperties.txt
+ gBidiMirrored.add('(', ')');
+ gBidiMirrored.add(')', '(');
+ gBidiMirrored.add('[', ']');
+ gBidiMirrored.add(']', '[');
+ gBidiMirrored.add('{', '}');
+ gBidiMirrored.add('}', '{');
+ gBidiMirrored.add('<', '>');
+ gBidiMirrored.add('>', '<');
+ gBidiMirrored.add(0x00ab, 0x00bb); // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+ gBidiMirrored.add(0x00bb, 0x00ab); // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+ gBidiMirrored.add(0x2039, 0x203a); // SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+ gBidiMirrored.add(0x203a, 0x2039); // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+ gBidiMirrored.add(0x2264, 0x2265); // LESS-THAN OR EQUAL TO
+ gBidiMirrored.add(0x2265, 0x2264); // GREATER-THAN OR EQUAL TO
}
TextLayoutShaper::~TextLayoutShaper() {
@@ -577,6 +599,31 @@
}
}
+ // Reverse "BiDi mirrored chars" in RTL mode only
+ // See: http://www.unicode.org/Public/6.0.0/ucd/extracted/DerivedBinaryProperties.txt
+ // This is a workaround because Harfbuzz is not able to do mirroring in all cases and
+ // script-run splitting with Harfbuzz is splitting on parenthesis
+ if (isRTL) {
+ for (ssize_t i = 0; i < ssize_t(count); i++) {
+ UChar ch = chars[i];
+ ssize_t index = gBidiMirrored.indexOfKey(ch);
+ // Skip non "BiDi mirrored" chars
+ if (index < 0) {
+ continue;
+ }
+ if (!useNormalizedString) {
+ useNormalizedString = true;
+ mNormalizedString.setTo(false /* not terminated*/, chars, count);
+ }
+ UChar result = gBidiMirrored.valueAt(index);
+ mNormalizedString.setCharAt(i, result);
+#if DEBUG_GLYPHS
+ ALOGD("Rewriting codepoint '%d' to '%d' at position %d",
+ ch, mNormalizedString[i], int(i));
+#endif
+ }
+ }
+
#if DEBUG_GLYPHS
if (useNormalizedString) {
ALOGD("Will use normalized string '%s', length = %d",
diff --git a/core/jni/android_media_ToneGenerator.cpp b/core/jni/android_media_ToneGenerator.cpp
index 53a0501..26e82aa 100644
--- a/core/jni/android_media_ToneGenerator.cpp
+++ b/core/jni/android_media_ToneGenerator.cpp
@@ -48,7 +48,7 @@
return false;
}
- return lpToneGen->startTone(toneType, durationMs);
+ return lpToneGen->startTone((ToneGenerator::tone_type) toneType, durationMs);
}
static void android_media_ToneGenerator_stopTone(JNIEnv *env, jobject thiz) {
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 990a617..e00970a 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -739,6 +739,11 @@
return IPCThreadState::self()->getCallingUid();
}
+static jint android_os_Binder_getOrigCallingUid(JNIEnv* env, jobject clazz)
+{
+ return IPCThreadState::self()->getOrigCallingUid();
+}
+
static jlong android_os_Binder_clearCallingIdentity(JNIEnv* env, jobject clazz)
{
return IPCThreadState::self()->clearCallingIdentity();
@@ -810,6 +815,7 @@
/* name, signature, funcPtr */
{ "getCallingPid", "()I", (void*)android_os_Binder_getCallingPid },
{ "getCallingUid", "()I", (void*)android_os_Binder_getCallingUid },
+ { "getOrigCallingUidNative", "()I", (void*)android_os_Binder_getOrigCallingUid },
{ "clearCallingIdentity", "()J", (void*)android_os_Binder_clearCallingIdentity },
{ "restoreCallingIdentity", "(J)V", (void*)android_os_Binder_restoreCallingIdentity },
{ "setThreadStrictModePolicy", "(I)V", (void*)android_os_Binder_setThreadStrictModePolicy },
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 1068b66..56af9aa 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -785,7 +785,7 @@
</plurals>
<plurals name="num_minutes_ago">
<item quantity="one" msgid="3306787433088810191">"iminithi elingu-1 edlule"</item>
- <item quantity="other" msgid="2176942008915455116">"amaminithi angu-<xliff:g id="COUNT">%d</xliff:g> adlule.."</item>
+ <item quantity="other" msgid="2176942008915455116">"<xliff:g id="COUNT">%d</xliff:g> amaminithi adlule.."</item>
</plurals>
<plurals name="num_hours_ago">
<item quantity="one" msgid="9150797944610821849">"ihora elingu-1 elidlule"</item>
@@ -822,7 +822,7 @@
</plurals>
<plurals name="abbrev_num_minutes_ago">
<item quantity="one" msgid="6361490147113871545">"iminithi elingu-1 edlule"</item>
- <item quantity="other" msgid="851164968597150710">"amaminithi angu-<xliff:g id="COUNT">%d</xliff:g> adlule"</item>
+ <item quantity="other" msgid="851164968597150710">"<xliff:g id="COUNT">%d</xliff:g> amaminithi adlule"</item>
</plurals>
<plurals name="abbrev_num_hours_ago">
<item quantity="one" msgid="4796212039724722116">"ihora elingu-1 elidlule"</item>
@@ -1111,7 +1111,7 @@
<string name="no_matches" msgid="8129421908915840737">"Akukho okufanayo"</string>
<string name="find_on_page" msgid="1946799233822820384">"Thola ekhasini"</string>
<plurals name="matches_found">
- <item quantity="one" msgid="8167147081136579439">"okufanayo okungu-1"</item>
+ <item quantity="one" msgid="8167147081136579439">"1 okufanayo"</item>
<item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> ku-<xliff:g id="TOTAL">%d</xliff:g>"</item>
</plurals>
<string name="action_mode_done" msgid="7217581640461922289">"Kwenziwe"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index b514bf5..16b7ff3 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2061,7 +2061,7 @@
</attr>
<!-- Direction of the text. A heuristic is used to determine the resolved text direction
of paragraphs. -->
- <attr name="textDirection" format="integer">
+ <attr name="textDirection" format="integer">
<!-- Default -->
<enum name="inherit" value="0" />
<!-- Default for the root view. The first strong directional character determines the
@@ -2072,16 +2072,12 @@
it is LTR if it contains any strong LTR characters. If there are neither, the
paragraph direction is the view’s resolved layout direction. -->
<enum name="anyRtl" value="2" />
- <!-- The paragraph direction is the same as the one held by a 60% majority of the
- characters. If there is no majority then the paragraph direction is the resolved
- layout direction of the View. -->
- <enum name="charCount" value="3" />
<!-- The paragraph direction is left to right. -->
- <enum name="ltr" value="4" />
+ <enum name="ltr" value="3" />
<!-- The paragraph direction is right to left. -->
- <enum name="rtl" value="5" />
+ <enum name="rtl" value="4" />
<!-- The paragraph direction is coming from the system Locale. -->
- <enum name="locale" value="6" />
+ <enum name="locale" value="5" />
</attr>
</declare-styleable>
diff --git a/core/tests/coretests/src/android/app/activity/BroadcastTest.java b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
index 4b1f9fd..d527c0d 100644
--- a/core/tests/coretests/src/android/app/activity/BroadcastTest.java
+++ b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
@@ -303,7 +303,8 @@
public void testSetSticky() throws Exception {
Intent intent = new Intent(LaunchpadActivity.BROADCAST_STICKY1, null);
intent.putExtra("test", LaunchpadActivity.DATA_1);
- ActivityManagerNative.getDefault().unbroadcastIntent(null, intent);
+ ActivityManagerNative.getDefault().unbroadcastIntent(null, intent,
+ Binder.getOrigCallingUser());
ActivityManagerNative.broadcastStickyIntent(intent, null);
addIntermediate("finished-broadcast");
@@ -320,7 +321,8 @@
ActivityManagerNative.broadcastStickyIntent(intent, null);
ActivityManagerNative.getDefault().unbroadcastIntent(
- null, new Intent(LaunchpadActivity.BROADCAST_STICKY1, null));
+ null, new Intent(LaunchpadActivity.BROADCAST_STICKY1, null),
+ Binder.getOrigCallingUser());
addIntermediate("finished-unbroadcast");
IntentFilter filter = new IntentFilter(LaunchpadActivity.BROADCAST_STICKY1);
diff --git a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
index 1df763a..b181122 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
@@ -24,6 +24,8 @@
import static android.net.NetworkStatsHistory.DataStreamUtils.readVarLong;
import static android.net.NetworkStatsHistory.DataStreamUtils.writeVarLong;
import static android.net.NetworkStatsHistory.Entry.UNKNOWN;
+import static android.net.TrafficStats.GB_IN_BYTES;
+import static android.net.TrafficStats.MB_IN_BYTES;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
@@ -50,10 +52,6 @@
private static final long TEST_START = 1194220800000L;
- private static final long KB_IN_BYTES = 1024;
- private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
- private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
-
private NetworkStatsHistory stats;
@Override
diff --git a/data/fonts/DroidSansArabic.ttf b/data/fonts/DroidSansArabic.ttf
new file mode 100644
index 0000000..bdefaac
--- /dev/null
+++ b/data/fonts/DroidSansArabic.ttf
Binary files differ
diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml
index 47cdae7..044de2f 100644
--- a/data/fonts/fallback_fonts.xml
+++ b/data/fonts/fallback_fonts.xml
@@ -25,6 +25,7 @@
<familyset>
<family>
<fileset>
+ <file>DroidSansArabic.ttf</file>
<file>DroidNaskh-Regular.ttf</file>
</fileset>
</family>
diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk
index 9dca329..6a9ed53 100644
--- a/data/fonts/fonts.mk
+++ b/data/fonts/fonts.mk
@@ -21,6 +21,7 @@
frameworks/base/data/fonts/Roboto-Bold.ttf:system/fonts/Roboto-Bold.ttf \
frameworks/base/data/fonts/Roboto-Italic.ttf:system/fonts/Roboto-Italic.ttf \
frameworks/base/data/fonts/Roboto-BoldItalic.ttf:system/fonts/Roboto-BoldItalic.ttf \
+ frameworks/base/data/fonts/DroidSansArabic.ttf:system/fonts/DroidSansArabic.ttf \
frameworks/base/data/fonts/DroidNaskh-Regular.ttf:system/fonts/DroidNaskh-Regular.ttf \
frameworks/base/data/fonts/DroidSansHebrew-Regular.ttf:system/fonts/DroidSansHebrew-Regular.ttf \
frameworks/base/data/fonts/DroidSansHebrew-Bold.ttf:system/fonts/DroidSansHebrew-Bold.ttf \
diff --git a/docs/html/index.jd b/docs/html/index.jd
index fdf860a..b9d6758 100644
--- a/docs/html/index.jd
+++ b/docs/html/index.jd
@@ -1,5 +1,5 @@
home=true
-metaDescription=The official site for Android developers. Provides the Android SDK and documentation for app developers and designers.
+page.metaDescription=The official site for Android developers. Provides the Android SDK and documentation for app developers and designers.
@jd:body
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index f285f5b..11c2427 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -487,6 +487,7 @@
final byte[] data = fp.getData();
int eSize = mType.mElement.mElements[component_number].getSizeBytes();
+ eSize *= mType.mElement.mArraySizes[component_number];
if (data.length != eSize) {
throw new RSIllegalArgumentException("Field packer sizelength " + data.length +
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index ad10832..bfe412c 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2008-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.
@@ -837,7 +837,8 @@
mRS.mErrorCallback.mErrorNum = subID;
mRS.mErrorCallback.run();
} else {
- //throw new RSRuntimeException("Received error num " + subID + ", details: " + e);
+ // Do not throw here. In these cases, we do not have
+ // a fatal error.
}
continue;
}
diff --git a/include/binder/IPCThreadState.h b/include/binder/IPCThreadState.h
index 3378d97..691ba2f 100644
--- a/include/binder/IPCThreadState.h
+++ b/include/binder/IPCThreadState.h
@@ -41,6 +41,7 @@
int getCallingPid();
int getCallingUid();
+ int getOrigCallingUid();
void setStrictModePolicy(int32_t policy);
int32_t getStrictModePolicy() const;
@@ -116,6 +117,7 @@
status_t mLastError;
pid_t mCallingPid;
uid_t mCallingUid;
+ uid_t mOrigCallingUid;
int32_t mStrictModePolicy;
int32_t mLastTransactionBinderFlags;
};
diff --git a/include/gui/DisplayEventReceiver.h b/include/gui/DisplayEventReceiver.h
index a281377..7bca8d6 100644
--- a/include/gui/DisplayEventReceiver.h
+++ b/include/gui/DisplayEventReceiver.h
@@ -95,6 +95,8 @@
* should be destroyed and getEvents() shouldn't be called again.
*/
ssize_t getEvents(Event* events, size_t count);
+ static ssize_t getEvents(const sp<BitTube>& dataChannel,
+ Event* events, size_t count);
/*
* setVsyncRate() sets the Event::VSync delivery rate. A value of
diff --git a/include/media/AudioEffect.h b/include/media/AudioEffect.h
index 1417416..7b0b443 100644
--- a/include/media/AudioEffect.h
+++ b/include/media/AudioEffect.h
@@ -226,8 +226,8 @@
AudioEffect(const effect_uuid_t *type,
const effect_uuid_t *uuid = NULL,
int32_t priority = 0,
- effect_callback_t cbf = 0,
- void* user = 0,
+ effect_callback_t cbf = NULL,
+ void* user = NULL,
int sessionId = 0,
audio_io_handle_t io = 0
);
@@ -238,8 +238,8 @@
AudioEffect(const char *typeStr,
const char *uuidStr = NULL,
int32_t priority = 0,
- effect_callback_t cbf = 0,
- void* user = 0,
+ effect_callback_t cbf = NULL,
+ void* user = NULL,
int sessionId = 0,
audio_io_handle_t io = 0
);
@@ -260,8 +260,8 @@
status_t set(const effect_uuid_t *type,
const effect_uuid_t *uuid = NULL,
int32_t priority = 0,
- effect_callback_t cbf = 0,
- void* user = 0,
+ effect_callback_t cbf = NULL,
+ void* user = NULL,
int sessionId = 0,
audio_io_handle_t io = 0
);
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index 76ec3b1..c8c5dba 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -155,8 +155,8 @@
uint32_t channelMask = AUDIO_CHANNEL_IN_MONO,
int frameCount = 0,
uint32_t flags = 0,
- callback_t cbf = 0,
- void* user = 0,
+ callback_t cbf = NULL,
+ void* user = NULL,
int notificationFrames = 0,
int sessionId = 0);
@@ -181,8 +181,8 @@
uint32_t channelMask = AUDIO_CHANNEL_IN_MONO,
int frameCount = 0,
uint32_t flags = 0,
- callback_t cbf = 0,
- void* user = 0,
+ callback_t cbf = NULL,
+ void* user = NULL,
int notificationFrames = 0,
bool threadCanCallJava = false,
int sessionId = 0);
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index 6b12c14..74a1e62 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -248,7 +248,7 @@
static sp<IAudioPolicyService> gAudioPolicyService;
// mapping between stream types and outputs
- static DefaultKeyedVector<int, audio_io_handle_t> gStreamOutputMap;
+ static DefaultKeyedVector<audio_stream_type_t, audio_io_handle_t> gStreamOutputMap;
// list of output descriptors containing cached parameters
// (sampling rate, framecount, channel count...)
static DefaultKeyedVector<audio_io_handle_t, OutputDescriptor *> gOutputs;
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index 98abfbd..02c85cd 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -148,8 +148,8 @@
int channelMask = 0,
int frameCount = 0,
uint32_t flags = 0,
- callback_t cbf = 0,
- void* user = 0,
+ callback_t cbf = NULL,
+ void* user = NULL,
int notificationFrames = 0,
int sessionId = 0);
@@ -180,8 +180,8 @@
int channelMask = 0,
const sp<IMemory>& sharedBuffer = 0,
uint32_t flags = 0,
- callback_t cbf = 0,
- void* user = 0,
+ callback_t cbf = NULL,
+ void* user = NULL,
int notificationFrames = 0,
int sessionId = 0);
@@ -204,8 +204,8 @@
int channelMask = 0,
int frameCount = 0,
uint32_t flags = 0,
- callback_t cbf = 0,
- void* user = 0,
+ callback_t cbf = NULL,
+ void* user = NULL,
int notificationFrames = 0,
const sp<IMemory>& sharedBuffer = 0,
bool threadCanCallJava = false,
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index 7c0d886..760595c 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -126,7 +126,7 @@
uint32_t *pSamplingRate,
audio_format_t *pFormat,
uint32_t *pChannels,
- uint32_t acoustics) = 0;
+ audio_in_acoustics_t acoustics) = 0;
virtual status_t closeInput(int input) = 0;
virtual status_t setStreamOutput(audio_stream_type_t stream, int output) = 0;
diff --git a/include/media/ToneGenerator.h b/include/media/ToneGenerator.h
index 7d890bd..df0c97e 100644
--- a/include/media/ToneGenerator.h
+++ b/include/media/ToneGenerator.h
@@ -154,7 +154,7 @@
ToneGenerator(audio_stream_type_t streamType, float volume, bool threadCanCallJava = false);
~ToneGenerator();
- bool startTone(int toneType, int durationMs = -1);
+ bool startTone(tone_type toneType, int durationMs = -1);
void stopTone();
bool isInited() { return (mState == TONE_IDLE)?false:true;}
@@ -274,7 +274,7 @@
bool prepareWave();
unsigned int numWaves(unsigned int segmentIdx);
void clearWaveGens();
- int getToneForRegion(int toneType);
+ tone_type getToneForRegion(tone_type toneType);
// WaveGenerator generates a single sine wave
class WaveGenerator {
diff --git a/include/media/Visualizer.h b/include/media/Visualizer.h
index 1a4cbca..60fa15b 100644
--- a/include/media/Visualizer.h
+++ b/include/media/Visualizer.h
@@ -66,8 +66,8 @@
* See AudioEffect constructor for details on parameters.
*/
Visualizer(int32_t priority = 0,
- effect_callback_t cbf = 0,
- void* user = 0,
+ effect_callback_t cbf = NULL,
+ void* user = NULL,
int sessionId = 0);
~Visualizer();
diff --git a/include/media/stagefright/AACWriter.h b/include/media/stagefright/AACWriter.h
index fa3ab8a..49397ee 100644
--- a/include/media/stagefright/AACWriter.h
+++ b/include/media/stagefright/AACWriter.h
@@ -34,7 +34,7 @@
virtual status_t addSource(const sp<MediaSource> &source);
virtual bool reachedEOS();
virtual status_t start(MetaData *params = NULL);
- virtual status_t stop();
+ virtual status_t stop() { return reset(); }
virtual status_t pause();
protected:
@@ -66,6 +66,7 @@
bool exceedsFileSizeLimit();
bool exceedsFileDurationLimit();
status_t writeAdtsHeader(uint32_t frameLength);
+ status_t reset();
DISALLOW_EVIL_CONSTRUCTORS(AACWriter);
};
diff --git a/include/media/stagefright/AMRWriter.h b/include/media/stagefright/AMRWriter.h
index 62d57b4..392f968 100644
--- a/include/media/stagefright/AMRWriter.h
+++ b/include/media/stagefright/AMRWriter.h
@@ -37,7 +37,7 @@
virtual status_t addSource(const sp<MediaSource> &source);
virtual bool reachedEOS();
virtual status_t start(MetaData *params = NULL);
- virtual status_t stop();
+ virtual status_t stop() { return reset(); }
virtual status_t pause();
protected:
@@ -60,6 +60,7 @@
status_t threadFunc();
bool exceedsFileSizeLimit();
bool exceedsFileDurationLimit();
+ status_t reset();
AMRWriter(const AMRWriter &);
AMRWriter &operator=(const AMRWriter &);
diff --git a/include/media/stagefright/AudioSource.h b/include/media/stagefright/AudioSource.h
index 2427e2f..79437bf 100644
--- a/include/media/stagefright/AudioSource.h
+++ b/include/media/stagefright/AudioSource.h
@@ -40,7 +40,7 @@
status_t initCheck() const;
virtual status_t start(MetaData *params = NULL);
- virtual status_t stop();
+ virtual status_t stop() { return reset(); }
virtual sp<MetaData> getFormat();
// Returns the maximum amplitude since last call.
@@ -97,6 +97,7 @@
void releaseQueuedFrames_l();
void waitOutstandingEncodingFrames_l();
+ status_t reset();
AudioSource(const AudioSource &);
AudioSource &operator=(const AudioSource &);
diff --git a/include/media/stagefright/CameraSource.h b/include/media/stagefright/CameraSource.h
index 446720b..5a35358 100644
--- a/include/media/stagefright/CameraSource.h
+++ b/include/media/stagefright/CameraSource.h
@@ -79,7 +79,7 @@
virtual ~CameraSource();
virtual status_t start(MetaData *params = NULL);
- virtual status_t stop();
+ virtual status_t stop() { return reset(); }
virtual status_t read(
MediaBuffer **buffer, const ReadOptions *options = NULL);
@@ -163,7 +163,6 @@
bool storeMetaDataInVideoBuffers);
virtual void startCameraRecording();
- virtual void stopCameraRecording();
virtual void releaseRecordingFrame(const sp<IMemory>& frame);
// Returns true if need to skip the current frame.
@@ -220,7 +219,9 @@
status_t checkFrameRate(const CameraParameters& params,
int32_t frameRate);
+ void stopCameraRecording();
void releaseCamera();
+ status_t reset();
CameraSource(const CameraSource &);
CameraSource &operator=(const CameraSource &);
diff --git a/include/media/stagefright/CameraSourceTimeLapse.h b/include/media/stagefright/CameraSourceTimeLapse.h
index b060691..0936da2 100644
--- a/include/media/stagefright/CameraSourceTimeLapse.h
+++ b/include/media/stagefright/CameraSourceTimeLapse.h
@@ -121,9 +121,6 @@
// Wrapper over CameraSource::read() to implement quick stop.
virtual status_t read(MediaBuffer **buffer, const ReadOptions *options = NULL);
- // For video camera case, just stops the camera's video recording.
- virtual void stopCameraRecording();
-
// mSkipCurrentFrame is set to true in dataCallbackTimestamp() if the current
// frame needs to be skipped and this function just returns the value of mSkipCurrentFrame.
virtual bool skipCurrentFrame(int64_t timestampUs);
diff --git a/include/media/stagefright/MPEG2TSWriter.h b/include/media/stagefright/MPEG2TSWriter.h
index e4c1c49..a7c9ecf 100644
--- a/include/media/stagefright/MPEG2TSWriter.h
+++ b/include/media/stagefright/MPEG2TSWriter.h
@@ -37,7 +37,7 @@
virtual status_t addSource(const sp<MediaSource> &source);
virtual status_t start(MetaData *param = NULL);
- virtual status_t stop();
+ virtual status_t stop() { return reset(); }
virtual status_t pause();
virtual bool reachedEOS();
virtual status_t dump(int fd, const Vector<String16>& args);
@@ -78,6 +78,7 @@
void writeAccessUnit(int32_t sourceIndex, const sp<ABuffer> &buffer);
ssize_t internalWrite(const void *data, size_t size);
+ status_t reset();
DISALLOW_EVIL_CONSTRUCTORS(MPEG2TSWriter);
};
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index 77166ed..0409b30 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -37,7 +37,7 @@
virtual status_t addSource(const sp<MediaSource> &source);
virtual status_t start(MetaData *param = NULL);
- virtual status_t stop();
+ virtual status_t stop() { return reset(); }
virtual status_t pause();
virtual bool reachedEOS();
virtual status_t dump(int fd, const Vector<String16>& args);
@@ -184,6 +184,7 @@
void writeLongitude(int degreex10000);
void sendSessionSummary();
void release();
+ status_t reset();
MPEG4Writer(const MPEG4Writer &);
MPEG4Writer &operator=(const MPEG4Writer &);
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index ffc546e..dd97ce4 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -76,7 +76,9 @@
// Left channel is in [0:15], right channel is in [16:31].
// Always read and write the combined pair atomically.
// For AudioTrack only, not used by AudioRecord.
- uint32_t volumeLR;
+private:
+ uint32_t mVolumeLR;
+public:
uint32_t sampleRate;
// NOTE: audio_track_cblk_t::frameSize is not equal to AudioTrack::frameSize() for
@@ -116,6 +118,17 @@
uint16_t getSendLevel_U4_12() const {
return mSendLevel;
}
+
+ // for AudioTrack client only, caller must limit to 0 <= volumeLR <= 0x10001000
+ void setVolumeLR(uint32_t volumeLR) {
+ mVolumeLR = volumeLR;
+ }
+
+ // for AudioFlinger only; the return value must be validated by the caller
+ uint32_t getVolumeLR() const {
+ return mVolumeLR;
+ }
+
};
diff --git a/include/utils/threads.h b/include/utils/threads.h
index ab3e8cd..b4a8b7c 100644
--- a/include/utils/threads.h
+++ b/include/utils/threads.h
@@ -526,6 +526,12 @@
// Do not call from this object's thread; will return WOULD_BLOCK in that case.
status_t join();
+#ifdef HAVE_ANDROID_OS
+ // Return the thread's kernel ID, same as the thread itself calling gettid() or
+ // androidGetTid(), or -1 if the thread is not running.
+ pid_t getTid() const;
+#endif
+
protected:
// exitPending() returns true if requestExit() has been called.
bool exitPending() const;
@@ -551,8 +557,10 @@
volatile bool mExitPending;
volatile bool mRunning;
sp<Thread> mHoldSelf;
-#if HAVE_ANDROID_OS
- int mTid;
+#ifdef HAVE_ANDROID_OS
+ // legacy for debugging, not used by getTid() as it is set by the child thread
+ // and so is not initialized until the child reaches that point
+ pid_t mTid;
#endif
};
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 629b899..b578a6c 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -371,6 +371,11 @@
return mCallingUid;
}
+int IPCThreadState::getOrigCallingUid()
+{
+ return mOrigCallingUid;
+}
+
int64_t IPCThreadState::clearCallingIdentity()
{
int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid;
@@ -641,6 +646,7 @@
{
pthread_setspecific(gTLS, this);
clearCaller();
+ mOrigCallingUid = mCallingUid;
mIn.setDataCapacity(256);
mOut.setDataCapacity(256);
}
@@ -987,6 +993,7 @@
mCallingPid = tr.sender_pid;
mCallingUid = tr.sender_euid;
+ mOrigCallingUid = tr.sender_euid;
int curPrio = getpriority(PRIO_PROCESS, mMyThreadId);
if (gDisableBackgroundScheduling) {
@@ -1045,6 +1052,7 @@
mCallingPid = origPid;
mCallingUid = origUid;
+ mOrigCallingUid = origUid;
IF_LOG_TRANSACTIONS() {
TextOutput::Bundle _b(alog);
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index 3b3ccaa..6a4763d 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -80,7 +80,13 @@
ssize_t DisplayEventReceiver::getEvents(DisplayEventReceiver::Event* events,
size_t count) {
- ssize_t size = mDataChannel->read(events, sizeof(events[0])*count);
+ return DisplayEventReceiver::getEvents(mDataChannel, events, count);
+}
+
+ssize_t DisplayEventReceiver::getEvents(const sp<BitTube>& dataChannel,
+ Event* events, size_t count)
+{
+ ssize_t size = dataChannel->read(events, sizeof(events[0])*count);
ALOGE_IF(size<0,
"DisplayEventReceiver::getEvents error (%s)",
strerror(-size));
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 95e0a18..5ec3983 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -26,6 +26,7 @@
ShapeCache.cpp \
SkiaColorFilter.cpp \
SkiaShader.cpp \
+ Snapshot.cpp \
TextureCache.cpp \
TextDropShadowCache.cpp
diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h
index 0ad0c2a..16a3d73 100644
--- a/libs/hwui/Debug.h
+++ b/libs/hwui/Debug.h
@@ -62,6 +62,9 @@
// Turn on to display debug info about the layer renderer
#define DEBUG_LAYER_RENDERER 0
+// Turn on to enable additional debugging in the font renderers
+#define DEBUG_FONT_RENDERER 0
+
// Turn on to dump display list state
#define DEBUG_DISPLAY_LIST 0
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 74efda2..3df105b 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -151,10 +151,12 @@
int32_t bX = 0, bY = 0;
for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
+#if DEBUG_FONT_RENDERER
if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
ALOGE("Skipping invalid index");
continue;
}
+#endif
uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
bitmap[bY * bitmapW + bX] = tempCol;
}
@@ -226,7 +228,7 @@
};
RenderGlyph render = gRenderGlyph[mode];
- if (positions == NULL) {
+ if (CC_LIKELY(positions == NULL)) {
SkFixed prevRsbDelta = 0;
float penX = x;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index bd213d5..afae70f 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -754,7 +754,7 @@
// TODO: See LayerRenderer.cpp::generateMesh() for important
// information about this implementation
- if (!layer->region.isEmpty()) {
+ if (CC_LIKELY(!layer->region.isEmpty())) {
size_t count;
const android::Rect* rects = layer->region.getArray(&count);
@@ -1398,7 +1398,7 @@
if (!texture) return;
const AutoTexture autoCleanup(texture);
- if (bitmap->getConfig() == SkBitmap::kA8_Config) {
+ if (CC_UNLIKELY(bitmap->getConfig() == SkBitmap::kA8_Config)) {
drawAlphaBitmap(texture, left, top, paint);
} else {
drawTextureRect(left, top, right, bottom, texture, paint);
@@ -1454,9 +1454,9 @@
float bottom = FLT_MIN;
#if RENDER_LAYERS_AS_REGIONS
- bool hasActiveLayer = hasLayer();
+ const bool hasActiveLayer = hasLayer();
#else
- bool hasActiveLayer = false;
+ const bool hasActiveLayer = false;
#endif
// TODO: Support the colors array
@@ -1541,7 +1541,7 @@
texture->setWrap(GL_CLAMP_TO_EDGE, true);
- if (mSnapshot->transform->isPureTranslate()) {
+ if (CC_LIKELY(mSnapshot->transform->isPureTranslate())) {
const float x = (int) floorf(dstLeft + mSnapshot->transform->getTranslateX() + 0.5f);
const float y = (int) floorf(dstTop + mSnapshot->transform->getTranslateY() + 0.5f);
@@ -1587,7 +1587,7 @@
const Patch* mesh = mCaches.patchCache.get(bitmap->width(), bitmap->height(),
right - left, bottom - top, xDivs, yDivs, colors, width, height, numColors);
- if (mesh && mesh->verticesCount > 0) {
+ if (CC_LIKELY(mesh && mesh->verticesCount > 0)) {
const bool pureTranslate = mSnapshot->transform->isPureTranslate();
#if RENDER_LAYERS_AS_REGIONS
// Mark the current layer dirty where we are going to draw the patch
@@ -1597,7 +1597,7 @@
const size_t count = mesh->quads.size();
for (size_t i = 0; i < count; i++) {
const Rect& bounds = mesh->quads.itemAt(i);
- if (pureTranslate) {
+ if (CC_LIKELY(pureTranslate)) {
const float x = (int) floorf(bounds.left + offsetX + 0.5f);
const float y = (int) floorf(bounds.top + offsetY + 0.5f);
dirtyLayer(x, y, x + bounds.getWidth(), y + bounds.getHeight());
@@ -1609,7 +1609,7 @@
}
#endif
- if (pureTranslate) {
+ if (CC_LIKELY(pureTranslate)) {
const float x = (int) floorf(left + mSnapshot->transform->getTranslateX() + 0.5f);
const float y = (int) floorf(top + mSnapshot->transform->getTranslateY() + 0.5f);
@@ -1637,7 +1637,7 @@
float inverseScaleX = 1.0f;
float inverseScaleY = 1.0f;
// The quad that we use needs to account for scaling.
- if (!mSnapshot->transform->isPureTranslate()) {
+ if (CC_UNLIKELY(!mSnapshot->transform->isPureTranslate())) {
Matrix4 *mat = mSnapshot->transform;
float m00 = mat->data[Matrix4::kScaleX];
float m01 = mat->data[Matrix4::kSkewY];
@@ -1743,7 +1743,7 @@
// The quad that we use for AA and hairlines needs to account for scaling. For hairlines
// the line on the screen should always be one pixel wide regardless of scale. For
// AA lines, we only want one pixel of translucent boundary around the quad.
- if (!mSnapshot->transform->isPureTranslate()) {
+ if (CC_UNLIKELY(!mSnapshot->transform->isPureTranslate())) {
Matrix4 *mat = mSnapshot->transform;
float m00 = mat->data[Matrix4::kScaleX];
float m01 = mat->data[Matrix4::kSkewY];
@@ -1751,8 +1751,8 @@
float m10 = mat->data[Matrix4::kSkewX];
float m11 = mat->data[Matrix4::kScaleX];
float m12 = mat->data[6];
- float scaleX = sqrt(m00*m00 + m01*m01);
- float scaleY = sqrt(m10*m10 + m11*m11);
+ float scaleX = sqrtf(m00 * m00 + m01 * m01);
+ float scaleY = sqrtf(m10 * m10 + m11 * m11);
inverseScaleX = (scaleX != 0) ? (inverseScaleX / scaleX) : 0;
inverseScaleY = (scaleY != 0) ? (inverseScaleY / scaleY) : 0;
if (inverseScaleX != 1.0f || inverseScaleY != 1.0f) {
@@ -1770,11 +1770,7 @@
setupDrawColor(paint->getColor(), alpha);
setupDrawColorFilter();
setupDrawShader();
- if (isAA) {
- setupDrawBlending(true, mode);
- } else {
- setupDrawBlending(mode);
- }
+ setupDrawBlending(isAA, mode);
setupDrawProgram();
setupDrawModelViewIdentity(true);
setupDrawColorUniforms();
@@ -1792,7 +1788,7 @@
Vertex* vertices = &lines[0];
AAVertex wLines[verticesCount];
AAVertex* aaVertices = &wLines[0];
- if (!isAA) {
+ if (CC_UNLIKELY(!isAA)) {
setupDrawVertices(vertices);
} else {
void* widthCoords = ((GLbyte*) aaVertices) + gVertexAAWidthOffset;
@@ -2152,9 +2148,9 @@
Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
#if RENDER_LAYERS_AS_REGIONS
- bool hasActiveLayer = hasLayer();
+ const bool hasActiveLayer = hasLayer();
#else
- bool hasActiveLayer = false;
+ const bool hasActiveLayer = false;
#endif
if (fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y,
@@ -2201,7 +2197,7 @@
const float oldX = x;
const float oldY = y;
const bool pureTranslate = mSnapshot->transform->isPureTranslate();
- if (pureTranslate) {
+ if (CC_LIKELY(pureTranslate)) {
x = (int) floorf(x + mSnapshot->transform->getTranslateX() + 0.5f);
y = (int) floorf(y + mSnapshot->transform->getTranslateY() + 0.5f);
}
@@ -2218,7 +2214,7 @@
SkXfermode::Mode mode;
getAlphaAndMode(paint, &alpha, &mode);
- if (mHasShadow) {
+ if (CC_UNLIKELY(mHasShadow)) {
mCaches.activeTexture(0);
mCaches.dropShadowCache.setFontRenderer(fontRenderer);
@@ -2277,9 +2273,9 @@
Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
#if RENDER_LAYERS_AS_REGIONS
- bool hasActiveLayer = hasLayer();
+ const bool hasActiveLayer = hasLayer();
#else
- bool hasActiveLayer = false;
+ const bool hasActiveLayer = false;
#endif
if (fontRenderer.renderText(paint, clip, text, 0, bytesCount, count, x, y,
@@ -2326,7 +2322,7 @@
layer->setAlpha(alpha, mode);
#if RENDER_LAYERS_AS_REGIONS
- if (!layer->region.isEmpty()) {
+ if (CC_LIKELY(!layer->region.isEmpty())) {
if (layer->region.isRect()) {
composeLayerRect(layer, layer->regionRect);
} else if (layer->mesh) {
@@ -2342,7 +2338,7 @@
setupDrawPureColorUniforms();
setupDrawColorFilterUniforms();
setupDrawTexture(layer->getTexture());
- if (mSnapshot->transform->isPureTranslate()) {
+ if (CC_LIKELY(mSnapshot->transform->isPureTranslate())) {
x = (int) floorf(x + mSnapshot->transform->getTranslateX() + 0.5f);
y = (int) floorf(y + mSnapshot->transform->getTranslateY() + 0.5f);
@@ -2502,7 +2498,7 @@
break;
}
- if (underlineWidth > 0.0f) {
+ if (CC_LIKELY(underlineWidth > 0.0f)) {
const float textSize = paintCopy.getTextSize();
const float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
@@ -2571,7 +2567,7 @@
texture->setWrap(GL_CLAMP_TO_EDGE, true);
- if (mSnapshot->transform->isPureTranslate()) {
+ if (CC_LIKELY(mSnapshot->transform->isPureTranslate())) {
const float x = (int) floorf(left + mSnapshot->transform->getTranslateX() + 0.5f);
const float y = (int) floorf(top + mSnapshot->transform->getTranslateY() + 0.5f);
@@ -2631,8 +2627,8 @@
// the blending, turn blending off here
// If the blend mode cannot be implemented using shaders, fall
// back to the default SrcOver blend mode instead
- if (mode > SkXfermode::kScreen_Mode) {
- if (mCaches.extensions.hasFramebufferFetch()) {
+ if CC_UNLIKELY((mode > SkXfermode::kScreen_Mode)) {
+ if (CC_UNLIKELY(mCaches.extensions.hasFramebufferFetch())) {
description.framebufferMode = mode;
description.swapSrcDst = swapSrcDst;
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 71fb8da..7854729 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -38,8 +38,8 @@
#define LAYER_SIZE 64
// Defines the size in bits of the stencil buffer
-// Note: We only want 1 bit, but in practice we'll get 8 bits on all GPUs
-// for the foreseeable future
+// Note: Only 1 bit is required for clipping but more bits are required
+// to properly implement the winding fill rule when rasterizing paths
#define STENCIL_BUFFER_SIZE 0
/**
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
new file mode 100644
index 0000000..a85362d
--- /dev/null
+++ b/libs/hwui/Snapshot.cpp
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+#include "Snapshot.h"
+
+#include <SkCanvas.h>
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors
+///////////////////////////////////////////////////////////////////////////////
+
+Snapshot::Snapshot(): flags(0), previous(NULL), layer(NULL), fbo(0),
+ invisible(false), empty(false) {
+
+ transform = &mTransformRoot;
+ clipRect = &mClipRectRoot;
+ region = NULL;
+}
+
+/**
+ * Copies the specified snapshot/ The specified snapshot is stored as
+ * the previous snapshot.
+ */
+Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags):
+ flags(0), previous(s), layer(NULL), fbo(s->fbo),
+ invisible(s->invisible), empty(false),
+ viewport(s->viewport), height(s->height) {
+
+ if (saveFlags & SkCanvas::kMatrix_SaveFlag) {
+ mTransformRoot.load(*s->transform);
+ transform = &mTransformRoot;
+ } else {
+ transform = s->transform;
+ }
+
+ if (saveFlags & SkCanvas::kClip_SaveFlag) {
+ mClipRectRoot.set(*s->clipRect);
+ clipRect = &mClipRectRoot;
+ } else {
+ clipRect = s->clipRect;
+ }
+
+ if (s->flags & Snapshot::kFlagFboTarget) {
+ flags |= Snapshot::kFlagFboTarget;
+ region = s->region;
+ } else {
+ region = NULL;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Clipping
+///////////////////////////////////////////////////////////////////////////////
+
+bool Snapshot::clip(float left, float top, float right, float bottom, SkRegion::Op op) {
+ Rect r(left, top, right, bottom);
+ transform->mapRect(r);
+ return clipTransformed(r, op);
+}
+
+bool Snapshot::clipTransformed(const Rect& r, SkRegion::Op op) {
+ bool clipped = false;
+
+ // NOTE: The unimplemented operations require support for regions
+ // Supporting regions would require using a stencil buffer instead
+ // of the scissor. The stencil buffer itself is not too expensive
+ // (memory cost excluded) but on fillrate limited devices, managing
+ // the stencil might have a negative impact on the framerate.
+ switch (op) {
+ case SkRegion::kDifference_Op:
+ break;
+ case SkRegion::kIntersect_Op:
+ clipped = clipRect->intersect(r);
+ if (!clipped) {
+ clipRect->setEmpty();
+ clipped = true;
+ }
+ break;
+ case SkRegion::kUnion_Op:
+ clipped = clipRect->unionWith(r);
+ break;
+ case SkRegion::kXOR_Op:
+ break;
+ case SkRegion::kReverseDifference_Op:
+ break;
+ case SkRegion::kReplace_Op:
+ clipRect->set(r);
+ clipped = true;
+ break;
+ }
+
+ if (clipped) {
+ flags |= Snapshot::kFlagClipSet;
+ }
+
+ return clipped;
+}
+
+void Snapshot::setClip(float left, float top, float right, float bottom) {
+ clipRect->set(left, top, right, bottom);
+ flags |= Snapshot::kFlagClipSet;
+}
+
+const Rect& Snapshot::getLocalClip() {
+ mat4 inverse;
+ inverse.loadInverse(*transform);
+
+ mLocalClip.set(*clipRect);
+ inverse.mapRect(mLocalClip);
+
+ return mLocalClip;
+}
+
+void Snapshot::resetClip(float left, float top, float right, float bottom) {
+ clipRect = &mClipRectRoot;
+ clipRect->set(left, top, right, bottom);
+ flags |= Snapshot::kFlagClipSet;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Transforms
+///////////////////////////////////////////////////////////////////////////////
+
+void Snapshot::resetTransform(float x, float y, float z) {
+ transform = &mTransformRoot;
+ transform->loadTranslate(x, y, z);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Queries
+///////////////////////////////////////////////////////////////////////////////
+
+bool Snapshot::isIgnored() const {
+ return invisible || empty;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index aff7b93..c94af7e 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -23,7 +23,7 @@
#include <utils/RefBase.h>
#include <ui/Region.h>
-#include <SkCanvas.h>
+#include <SkRegion.h>
#include "Layer.h"
#include "Matrix.h"
@@ -43,43 +43,12 @@
*/
class Snapshot: public LightRefBase<Snapshot> {
public:
- Snapshot(): flags(0), previous(NULL), layer(NULL), fbo(0), invisible(false), empty(false) {
- transform = &mTransformRoot;
- clipRect = &mClipRectRoot;
- region = NULL;
- }
+
+ Snapshot();
+ Snapshot(const sp<Snapshot>& s, int saveFlags);
/**
- * Copies the specified snapshot/ The specified snapshot is stored as
- * the previous snapshot.
- */
- Snapshot(const sp<Snapshot>& s, int saveFlags):
- flags(0), previous(s), layer(NULL), fbo(s->fbo),
- invisible(s->invisible), empty(false), viewport(s->viewport), height(s->height) {
- if (saveFlags & SkCanvas::kMatrix_SaveFlag) {
- mTransformRoot.load(*s->transform);
- transform = &mTransformRoot;
- } else {
- transform = s->transform;
- }
-
- if (saveFlags & SkCanvas::kClip_SaveFlag) {
- mClipRectRoot.set(*s->clipRect);
- clipRect = &mClipRectRoot;
- } else {
- clipRect = s->clipRect;
- }
-
- if (s->flags & Snapshot::kFlagFboTarget) {
- flags |= Snapshot::kFlagFboTarget;
- region = s->region;
- } else {
- region = NULL;
- }
- }
-
- /**
- * Various flags set on #flags.
+ * Various flags set on ::flags.
*/
enum Flags {
/**
@@ -115,87 +84,41 @@
* by this snapshot's trasnformation.
*/
bool clip(float left, float top, float right, float bottom,
- SkRegion::Op op = SkRegion::kIntersect_Op) {
- Rect r(left, top, right, bottom);
- transform->mapRect(r);
- return clipTransformed(r, op);
- }
+ SkRegion::Op op = SkRegion::kIntersect_Op);
/**
* Modifies the current clip with the new clip rectangle and
* the specified operation. The specified rectangle is considered
* already transformed.
*/
- bool clipTransformed(const Rect& r, SkRegion::Op op = SkRegion::kIntersect_Op) {
- bool clipped = false;
-
- // NOTE: The unimplemented operations require support for regions
- // Supporting regions would require using a stencil buffer instead
- // of the scissor. The stencil buffer itself is not too expensive
- // (memory cost excluded) but on fillrate limited devices, managing
- // the stencil might have a negative impact on the framerate.
- switch (op) {
- case SkRegion::kDifference_Op:
- break;
- case SkRegion::kIntersect_Op:
- clipped = clipRect->intersect(r);
- if (!clipped) {
- clipRect->setEmpty();
- clipped = true;
- }
- break;
- case SkRegion::kUnion_Op:
- clipped = clipRect->unionWith(r);
- break;
- case SkRegion::kXOR_Op:
- break;
- case SkRegion::kReverseDifference_Op:
- break;
- case SkRegion::kReplace_Op:
- clipRect->set(r);
- clipped = true;
- break;
- }
-
- if (clipped) {
- flags |= Snapshot::kFlagClipSet;
- }
-
- return clipped;
- }
+ bool clipTransformed(const Rect& r, SkRegion::Op op = SkRegion::kIntersect_Op);
/**
* Sets the current clip.
*/
- void setClip(float left, float top, float right, float bottom) {
- clipRect->set(left, top, right, bottom);
- flags |= Snapshot::kFlagClipSet;
- }
+ void setClip(float left, float top, float right, float bottom);
- const Rect& getLocalClip() {
- mat4 inverse;
- inverse.loadInverse(*transform);
+ /**
+ * Returns the current clip in local coordinates. The clip rect is
+ * transformed by the inverse transform matrix.
+ */
+ const Rect& getLocalClip();
- mLocalClip.set(*clipRect);
- inverse.mapRect(mLocalClip);
+ /**
+ * Resets the clip to the specified rect.
+ */
+ void resetClip(float left, float top, float right, float bottom);
- return mLocalClip;
- }
+ /**
+ * Resets the current transform to a pure 3D translation.
+ */
+ void resetTransform(float x, float y, float z);
- void resetTransform(float x, float y, float z) {
- transform = &mTransformRoot;
- transform->loadTranslate(x, y, z);
- }
-
- void resetClip(float left, float top, float right, float bottom) {
- clipRect = &mClipRectRoot;
- clipRect->set(left, top, right, bottom);
- flags |= Snapshot::kFlagClipSet;
- }
-
- bool isIgnored() const {
- return invisible || empty;
- }
+ /**
+ * Indicates whether this snapshot should be ignored. A snapshot
+ * is typicalled ignored if its layer is invisible or empty.
+ */
+ bool isIgnored() const;
/**
* Dirty flags.
@@ -209,6 +132,8 @@
/**
* Only set when the flag kFlagIsLayer is set.
+ *
+ * This snapshot does not own the layer, this pointer must not be freed.
*/
Layer* layer;
@@ -249,17 +174,26 @@
/**
* Local transformation. Holds the current translation, scale and
* rotation values.
+ *
+ * This is a reference to a matrix owned by this snapshot or another
+ * snapshot. This pointer must not be freed. See ::mTransformRoot.
*/
mat4* transform;
/**
* Current clip region. The clip is stored in canvas-space coordinates,
* (screen-space coordinates in the regular case.)
+ *
+ * This is a reference to a rect owned by this snapshot or another
+ * snapshot. This pointer must not be freed. See ::mClipRectRoot.
*/
Rect* clipRect;
/**
* The ancestor layer's dirty region.
+ *
+ * This is a reference to a region owned by a layer. This pointer must
+ * not be freed.
*/
Region* region;
diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp
index 972e3d6..fd85b07 100644
--- a/libs/rs/rsAllocation.cpp
+++ b/libs/rs/rsAllocation.cpp
@@ -124,7 +124,8 @@
}
const Element * e = mHal.state.type->getElement()->getField(cIdx);
- if (sizeBytes != e->getSizeBytes()) {
+ uint32_t elemArraySize = mHal.state.type->getElement()->getFieldArraySize(cIdx);
+ if (sizeBytes != e->getSizeBytes() * elemArraySize) {
ALOGE("Error Allocation::subElementData data size %zu does not match field size %zu.", sizeBytes, e->getSizeBytes());
rsc->setError(RS_ERROR_BAD_VALUE, "subElementData bad size.");
return;
@@ -157,8 +158,8 @@
}
const Element * e = mHal.state.type->getElement()->getField(cIdx);
-
- if (sizeBytes != e->getSizeBytes()) {
+ uint32_t elemArraySize = mHal.state.type->getElement()->getFieldArraySize(cIdx);
+ if (sizeBytes != e->getSizeBytes() * elemArraySize) {
ALOGE("Error Allocation::subElementData data size %zu does not match field size %zu.", sizeBytes, e->getSizeBytes());
rsc->setError(RS_ERROR_BAD_VALUE, "subElementData bad size.");
return;
diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp
index afc8ba0..b4eb995 100644
--- a/libs/rs/rsScriptC.cpp
+++ b/libs/rs/rsScriptC.cpp
@@ -206,7 +206,6 @@
return false;
}
- rsAssert(bcWrapper.getHeaderVersion() == 0);
if (bcWrapper.getBCFileType() == bcinfo::BC_WRAPPER) {
sdkVersion = bcWrapper.getTargetAPI();
}
@@ -323,7 +322,7 @@
if (!s->runCompiler(rsc, resName, cacheDir, (uint8_t *)text, text_length)) {
// Error during compile, destroy s and return null.
- delete s;
+ ObjectBase::checkDelete(s);
return NULL;
}
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
index 544ab74..24cf504 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -98,7 +98,8 @@
LOCAL_C_INCLUDES += \
external/zlib \
- external/icu4c/common
+ external/icu4c/common \
+ bionic/libc/private
LOCAL_LDLIBS += -lpthread
@@ -114,7 +115,10 @@
ifeq ($(TARGET_OS),linux)
include $(CLEAR_VARS)
-LOCAL_C_INCLUDES += external/zlib external/icu4c/common
+LOCAL_C_INCLUDES += \
+ external/zlib \
+ external/icu4c/common \
+ bionic/libc/private
LOCAL_LDLIBS := -lrt -ldl -lpthread
LOCAL_MODULE := libutils
LOCAL_SRC_FILES := $(commonSources) BackupData.cpp BackupHelpers.cpp
diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp
index e343c62..ab207f5 100644
--- a/libs/utils/Threads.cpp
+++ b/libs/utils/Threads.cpp
@@ -34,6 +34,9 @@
# include <pthread.h>
# include <sched.h>
# include <sys/resource.h>
+#ifdef HAVE_ANDROID_OS
+# include <bionic_pthread.h>
+#endif
#elif defined(HAVE_WIN32_THREADS)
# include <windows.h>
# include <stdint.h>
@@ -86,7 +89,7 @@
char * threadName;
// we use this trampoline when we need to set the priority with
- // nice/setpriority.
+ // nice/setpriority, and name with prctl.
static int trampoline(const thread_data_t* t) {
thread_func_t f = t->entryFunction;
void* u = t->userData;
@@ -141,8 +144,13 @@
#ifdef HAVE_ANDROID_OS /* valgrind is rejecting RT-priority create reqs */
if (threadPriority != PRIORITY_DEFAULT || threadName != NULL) {
- // We could avoid the trampoline if there was a way to get to the
- // android_thread_id_t (pid) from pthread_t
+ // Now that the pthread_t has a method to find the associated
+ // android_thread_id_t (pid) from pthread_t, it would be possible to avoid
+ // this trampoline in some cases as the parent could set the properties
+ // for the child. However, there would be a race condition because the
+ // child becomes ready immediately, and it doesn't work for the name.
+ // prctl(PR_SET_NAME) only works for self; prctl(PR_SET_THREAD_NAME) was
+ // proposed but not yet accepted.
thread_data_t* t = new thread_data_t;
t->priority = threadPriority;
t->threadName = threadName ? strdup(threadName) : NULL;
@@ -178,6 +186,13 @@
return 1;
}
+#ifdef HAVE_ANDROID_OS
+static pthread_t android_thread_id_t_to_pthread(android_thread_id_t thread)
+{
+ return (pthread_t) thread;
+}
+#endif
+
android_thread_id_t androidGetThreadId()
{
return (android_thread_id_t)pthread_self();
@@ -909,6 +924,23 @@
return mStatus;
}
+#ifdef HAVE_ANDROID_OS
+pid_t Thread::getTid() const
+{
+ // mTid is not defined until the child initializes it, and the caller may need it earlier
+ Mutex::Autolock _l(mLock);
+ pid_t tid;
+ if (mRunning) {
+ pthread_t pthread = android_thread_id_t_to_pthread(mThread);
+ tid = __pthread_gettid(pthread);
+ } else {
+ ALOGW("Thread (this=%p): getTid() is undefined before run()", this);
+ tid = -1;
+ }
+ return tid;
+}
+#endif
+
bool Thread::exitPending() const
{
Mutex::Autolock _l(mLock);
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index a720c0a..85d99c1 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -204,18 +204,24 @@
/** MPEG4 media file format*/
public static final int MPEG_4 = 2;
- /** The following formats are audio only .aac or .amr formats **/
- /** @deprecated Deprecated in favor of AMR_NB */
- /** Deprecated in favor of MediaRecorder.OutputFormat.AMR_NB */
- /** AMR NB file format */
+ /** The following formats are audio only .aac or .amr formats */
+
+ /**
+ * AMR NB file format
+ * @deprecated Deprecated in favor of MediaRecorder.OutputFormat.AMR_NB
+ */
public static final int RAW_AMR = 3;
+
/** AMR NB file format */
public static final int AMR_NB = 3;
+
/** AMR WB file format */
public static final int AMR_WB = 4;
+
/** @hide AAC ADIF file format */
public static final int AAC_ADIF = 5;
- /** @hide AAC ADTS file format */
+
+ /** AAC ADTS file format */
public static final int AAC_ADTS = 6;
/** @hide Stream over a socket, limited to a single stream */
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index b640e9a..1c13fff 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -314,10 +314,8 @@
private final String mExternalStoragePath;
- // WARNING: Bulk inserts sounded like a great idea and gave us a good performance improvement,
- // but unfortunately it also introduced a number of bugs. All the known bugs were fixed,
- // but we need more testing before enabling.
- private static final boolean ENABLE_BULK_INSERTS = false;
+ /** whether to use bulk inserts or individual inserts for each item */
+ private static final boolean ENABLE_BULK_INSERTS = true;
// used when scanning the image database so we know whether we have to prune
// old thumbnail files
diff --git a/media/libeffects/preprocessing/Android.mk b/media/libeffects/preprocessing/Android.mk
index 77d40b6..7f7c7e1 100755
--- a/media/libeffects/preprocessing/Android.mk
+++ b/media/libeffects/preprocessing/Android.mk
@@ -13,7 +13,7 @@
LOCAL_C_INCLUDES += \
external/webrtc/src \
external/webrtc/src/modules/interface \
- external/webrtc/src/modules/audio_processing/main/interface \
+ external/webrtc/src/modules/audio_processing/interface \
system/media/audio_effects/include
LOCAL_C_INCLUDES += $(call include-path-for, speex)
diff --git a/media/libeffects/preprocessing/PreProcessing.cpp b/media/libeffects/preprocessing/PreProcessing.cpp
index e988e06..9fd6764 100755
--- a/media/libeffects/preprocessing/PreProcessing.cpp
+++ b/media/libeffects/preprocessing/PreProcessing.cpp
@@ -24,8 +24,8 @@
#include <audio_effects/effect_aec.h>
#include <audio_effects/effect_agc.h>
#include <audio_effects/effect_ns.h>
-#include "modules/interface/module_common_types.h"
-#include "modules/audio_processing/main/interface/audio_processing.h"
+#include <module_common_types.h>
+#include <audio_processing.h>
#include "speex/speex_resampler.h"
@@ -220,8 +220,8 @@
// Automatic Gain Control (AGC)
//------------------------------------------------------------------------------
-static const int kAgcDefaultTargetLevel = 0;
-static const int kAgcDefaultCompGain = 90;
+static const int kAgcDefaultTargetLevel = 3;
+static const int kAgcDefaultCompGain = 9;
static const bool kAgcDefaultLimiter = true;
int AgcInit (preproc_effect_t *effect)
diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp
index 6639d06..a242846 100644
--- a/media/libmedia/AudioEffect.cpp
+++ b/media/libmedia/AudioEffect.cpp
@@ -342,7 +342,7 @@
{
ALOGW("IEffect died");
mStatus = NO_INIT;
- if (mCbf) {
+ if (mCbf != NULL) {
status_t status = DEAD_OBJECT;
mCbf(EVENT_ERROR, mUserData, &status);
}
@@ -363,7 +363,7 @@
mStatus = ALREADY_EXISTS;
}
}
- if (mCbf) {
+ if (mCbf != NULL) {
mCbf(EVENT_CONTROL_STATUS_CHANGED, mUserData, &controlGranted);
}
}
@@ -373,7 +373,7 @@
ALOGV("enableStatusChanged %p enabled %d mCbf %p", this, enabled, mCbf);
if (mStatus == ALREADY_EXISTS) {
mEnabled = enabled;
- if (mCbf) {
+ if (mCbf != NULL) {
mCbf(EVENT_ENABLE_STATUS_CHANGED, mUserData, &enabled);
}
}
@@ -389,7 +389,7 @@
return;
}
- if (mCbf && cmdCode == EFFECT_CMD_SET_PARAM) {
+ if (mCbf != NULL && cmdCode == EFFECT_CMD_SET_PARAM) {
effect_param_t *cmd = (effect_param_t *)cmdData;
cmd->status = *(int32_t *)replyData;
mCbf(EVENT_PARAMETER_CHANGED, mUserData, cmd);
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index 2b3ea38..c96bc76 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -206,7 +206,7 @@
return status;
}
- if (cbf != 0) {
+ if (cbf != NULL) {
mClientRecordThread = new ClientRecordThread(*this, threadCanCallJava);
}
@@ -387,7 +387,7 @@
status_t AudioRecord::setMarkerPosition(uint32_t marker)
{
- if (mCbf == 0) return INVALID_OPERATION;
+ if (mCbf == NULL) return INVALID_OPERATION;
mMarkerPosition = marker;
mMarkerReached = false;
@@ -397,7 +397,7 @@
status_t AudioRecord::getMarkerPosition(uint32_t *marker)
{
- if (marker == 0) return BAD_VALUE;
+ if (marker == NULL) return BAD_VALUE;
*marker = mMarkerPosition;
@@ -406,7 +406,7 @@
status_t AudioRecord::setPositionUpdatePeriod(uint32_t updatePeriod)
{
- if (mCbf == 0) return INVALID_OPERATION;
+ if (mCbf == NULL) return INVALID_OPERATION;
uint32_t curPosition;
getPosition(&curPosition);
@@ -418,7 +418,7 @@
status_t AudioRecord::getPositionUpdatePeriod(uint32_t *updatePeriod)
{
- if (updatePeriod == 0) return BAD_VALUE;
+ if (updatePeriod == NULL) return BAD_VALUE;
*updatePeriod = mUpdatePeriod;
@@ -427,7 +427,7 @@
status_t AudioRecord::getPosition(uint32_t *position)
{
- if (position == 0) return BAD_VALUE;
+ if (position == NULL) return BAD_VALUE;
AutoMutex lock(mLock);
*position = mCblk->user;
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index 124032b..110a294 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -35,7 +35,8 @@
sp<AudioSystem::AudioFlingerClient> AudioSystem::gAudioFlingerClient;
audio_error_callback AudioSystem::gAudioErrorCallback = NULL;
// Cached values
-DefaultKeyedVector<int, audio_io_handle_t> AudioSystem::gStreamOutputMap(0);
+
+DefaultKeyedVector<audio_stream_type_t, audio_io_handle_t> AudioSystem::gStreamOutputMap(0);
DefaultKeyedVector<audio_io_handle_t, AudioSystem::OutputDescriptor *> AudioSystem::gOutputs(0);
// Cached values for recording queries, all protected by gLock
@@ -224,7 +225,7 @@
gLock.lock();
outputDesc = AudioSystem::gOutputs.valueFor(output);
- if (outputDesc == 0) {
+ if (outputDesc == NULL) {
ALOGV("getOutputSamplingRate() no output descriptor for output %d in gOutputs", output);
gLock.unlock();
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
@@ -262,7 +263,7 @@
gLock.lock();
outputDesc = AudioSystem::gOutputs.valueFor(output);
- if (outputDesc == 0) {
+ if (outputDesc == NULL) {
gLock.unlock();
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
@@ -293,7 +294,7 @@
gLock.lock();
outputDesc = AudioSystem::gOutputs.valueFor(output);
- if (outputDesc == 0) {
+ if (outputDesc == NULL) {
gLock.unlock();
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
@@ -404,7 +405,7 @@
void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, int ioHandle, void *param2) {
ALOGV("ioConfigChanged() event %d", event);
OutputDescriptor *desc;
- uint32_t stream;
+ audio_stream_type_t stream;
if (ioHandle == 0) return;
@@ -412,8 +413,8 @@
switch (event) {
case STREAM_CONFIG_CHANGED:
- if (param2 == 0) break;
- stream = *(uint32_t *)param2;
+ if (param2 == NULL) break;
+ stream = *(audio_stream_type_t *)param2;
ALOGV("ioConfigChanged() STREAM_CONFIG_CHANGED stream %d, output %d", stream, ioHandle);
if (gStreamOutputMap.indexOfKey(stream) >= 0) {
gStreamOutputMap.replaceValueFor(stream, ioHandle);
@@ -424,7 +425,7 @@
ALOGV("ioConfigChanged() opening already existing output! %d", ioHandle);
break;
}
- if (param2 == 0) break;
+ if (param2 == NULL) break;
desc = (OutputDescriptor *)param2;
OutputDescriptor *outputDesc = new OutputDescriptor(*desc);
@@ -453,7 +454,7 @@
ALOGW("ioConfigChanged() modifying unknow output! %d", ioHandle);
break;
}
- if (param2 == 0) break;
+ if (param2 == NULL) break;
desc = (OutputDescriptor *)param2;
ALOGV("ioConfigChanged() new config for output %d samplingRate %d, format %d channels %d frameCount %d latency %d",
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 17e3d4b..8c33f41 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -257,7 +257,7 @@
return status;
}
- if (cbf != 0) {
+ if (cbf != NULL) {
mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);
}
@@ -501,7 +501,7 @@
mVolume[LEFT] = left;
mVolume[RIGHT] = right;
- mCblk->volumeLR = (uint32_t(uint16_t(right * 0x1000)) << 16) | uint16_t(left * 0x1000);
+ mCblk->setVolumeLR((uint32_t(uint16_t(right * 0x1000)) << 16) | uint16_t(left * 0x1000));
return NO_ERROR;
}
@@ -604,13 +604,13 @@
status_t AudioTrack::getLoop(uint32_t *loopStart, uint32_t *loopEnd, int *loopCount)
{
AutoMutex lock(mLock);
- if (loopStart != 0) {
+ if (loopStart != NULL) {
*loopStart = mCblk->loopStart;
}
- if (loopEnd != 0) {
+ if (loopEnd != NULL) {
*loopEnd = mCblk->loopEnd;
}
- if (loopCount != 0) {
+ if (loopCount != NULL) {
if (mCblk->loopCount < 0) {
*loopCount = -1;
} else {
@@ -623,7 +623,7 @@
status_t AudioTrack::setMarkerPosition(uint32_t marker)
{
- if (mCbf == 0) return INVALID_OPERATION;
+ if (mCbf == NULL) return INVALID_OPERATION;
mMarkerPosition = marker;
mMarkerReached = false;
@@ -633,7 +633,7 @@
status_t AudioTrack::getMarkerPosition(uint32_t *marker)
{
- if (marker == 0) return BAD_VALUE;
+ if (marker == NULL) return BAD_VALUE;
*marker = mMarkerPosition;
@@ -642,7 +642,7 @@
status_t AudioTrack::setPositionUpdatePeriod(uint32_t updatePeriod)
{
- if (mCbf == 0) return INVALID_OPERATION;
+ if (mCbf == NULL) return INVALID_OPERATION;
uint32_t curPosition;
getPosition(&curPosition);
@@ -654,7 +654,7 @@
status_t AudioTrack::getPositionUpdatePeriod(uint32_t *updatePeriod)
{
- if (updatePeriod == 0) return BAD_VALUE;
+ if (updatePeriod == NULL) return BAD_VALUE;
*updatePeriod = mUpdatePeriod;
@@ -679,7 +679,7 @@
status_t AudioTrack::getPosition(uint32_t *position)
{
- if (position == 0) return BAD_VALUE;
+ if (position == NULL) return BAD_VALUE;
AutoMutex lock(mLock);
*position = mFlushed ? 0 : mCblk->server;
@@ -837,7 +837,7 @@
mCblk->stepUser(mCblk->frameCount);
}
- mCblk->volumeLR = (uint32_t(uint16_t(mVolume[RIGHT] * 0x1000)) << 16) | uint16_t(mVolume[LEFT] * 0x1000);
+ mCblk->setVolumeLR((uint32_t(uint16_t(mVolume[RIGHT] * 0x1000)) << 16) | uint16_t(mVolume[LEFT] * 0x1000));
mCblk->setSendLevel(mSendLevel);
mAudioTrack->attachAuxEffect(mAuxEffectId);
mCblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
@@ -1319,8 +1319,8 @@
audio_track_cblk_t::audio_track_cblk_t()
: lock(Mutex::SHARED), cv(Condition::SHARED), user(0), server(0),
- userBase(0), serverBase(0), buffers(0), frameCount(0),
- loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), volumeLR(0),
+ userBase(0), serverBase(0), buffers(NULL), frameCount(0),
+ loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), mVolumeLR(0x10001000),
mSendLevel(0), flags(0)
{
}
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index 0d442ef..fc5520f 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -432,7 +432,7 @@
uint32_t *pSamplingRate,
audio_format_t *pFormat,
uint32_t *pChannels,
- uint32_t acoustics)
+ audio_in_acoustics_t acoustics)
{
Parcel data, reply;
uint32_t devices = pDevices ? *pDevices : 0;
@@ -445,7 +445,7 @@
data.writeInt32(samplingRate);
data.writeInt32(format);
data.writeInt32(channels);
- data.writeInt32(acoustics);
+ data.writeInt32((int32_t) acoustics);
remote()->transact(OPEN_INPUT, data, &reply);
int input = reply.readInt32();
devices = reply.readInt32();
@@ -640,7 +640,7 @@
*id = tmp;
}
tmp = reply.readInt32();
- if (enabled) {
+ if (enabled != NULL) {
*enabled = tmp;
}
effect = interface_cast<IEffect>(reply.readStrongBinder());
@@ -881,13 +881,13 @@
uint32_t samplingRate = data.readInt32();
audio_format_t format = (audio_format_t) data.readInt32();
uint32_t channels = data.readInt32();
- uint32_t acoutics = data.readInt32();
+ audio_in_acoustics_t acoustics = (audio_in_acoustics_t) data.readInt32();
int input = openInput(&devices,
&samplingRate,
&format,
&channels,
- acoutics);
+ acoustics);
reply->writeInt32(input);
reply->writeInt32(devices);
reply->writeInt32(samplingRate);
diff --git a/media/libmedia/IAudioFlingerClient.cpp b/media/libmedia/IAudioFlingerClient.cpp
index 5a3f250..9458bc0 100644
--- a/media/libmedia/IAudioFlingerClient.cpp
+++ b/media/libmedia/IAudioFlingerClient.cpp
@@ -73,7 +73,7 @@
CHECK_INTERFACE(IAudioFlingerClient, data, reply);
int event = data.readInt32();
int ioHandle = data.readInt32();
- void *param2 = 0;
+ void *param2 = NULL;
AudioSystem::OutputDescriptor desc;
uint32_t stream;
if (event == AudioSystem::STREAM_CONFIG_CHANGED) {
diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp
index 5ceb912..e6e989d 100644
--- a/media/libmedia/ToneGenerator.cpp
+++ b/media/libmedia/ToneGenerator.cpp
@@ -751,7 +751,7 @@
// Used by ToneGenerator::getToneForRegion() to convert user specified supervisory tone type
// to actual tone for current region.
-const unsigned char ToneGenerator::sToneMappingTable[NUM_REGIONS-1][NUM_SUP_TONES] = {
+const unsigned char /*tone_type*/ ToneGenerator::sToneMappingTable[NUM_REGIONS-1][NUM_SUP_TONES] = {
{ // ANSI
TONE_ANSI_DIAL, // TONE_SUP_DIAL
TONE_ANSI_BUSY, // TONE_SUP_BUSY
@@ -811,9 +811,9 @@
mThreadCanCallJava = threadCanCallJava;
mStreamType = streamType;
mVolume = volume;
- mpAudioTrack = 0;
- mpToneDesc = 0;
- mpNewToneDesc = 0;
+ mpAudioTrack = NULL;
+ mpToneDesc = NULL;
+ mpNewToneDesc = NULL;
// Generate tone by chunks of 20 ms to keep cadencing precision
mProcessSize = (mSamplingRate * 20) / 1000;
@@ -855,7 +855,7 @@
ToneGenerator::~ToneGenerator() {
ALOGV("ToneGenerator destructor\n");
- if (mpAudioTrack) {
+ if (mpAudioTrack != NULL) {
stopTone();
ALOGV("Delete Track: %p\n", mpAudioTrack);
delete mpAudioTrack;
@@ -878,7 +878,7 @@
// none
//
////////////////////////////////////////////////////////////////////////////////
-bool ToneGenerator::startTone(int toneType, int durationMs) {
+bool ToneGenerator::startTone(tone_type toneType, int durationMs) {
bool lResult = false;
status_t lStatus;
@@ -1012,7 +1012,7 @@
if (mpAudioTrack) {
delete mpAudioTrack;
- mpAudioTrack = 0;
+ mpAudioTrack = NULL;
}
// Open audio track in mono, PCM 16bit, default sampling rate, default buffer size
@@ -1048,7 +1048,7 @@
if (mpAudioTrack) {
ALOGV("Delete Track I: %p\n", mpAudioTrack);
delete mpAudioTrack;
- mpAudioTrack = 0;
+ mpAudioTrack = NULL;
}
return false;
@@ -1317,7 +1317,7 @@
bool ToneGenerator::prepareWave() {
unsigned int segmentIdx = 0;
- if (!mpNewToneDesc) {
+ if (mpNewToneDesc == NULL) {
return false;
}
@@ -1434,13 +1434,13 @@
// none
//
////////////////////////////////////////////////////////////////////////////////
-int ToneGenerator::getToneForRegion(int toneType) {
- int regionTone;
+ToneGenerator::tone_type ToneGenerator::getToneForRegion(tone_type toneType) {
+ tone_type regionTone;
if (mRegion == CEPT || toneType < FIRST_SUP_TONE || toneType > LAST_SUP_TONE) {
regionTone = toneType;
} else {
- regionTone = sToneMappingTable[mRegion][toneType - FIRST_SUP_TONE];
+ regionTone = (tone_type) sToneMappingTable[mRegion][toneType - FIRST_SUP_TONE];
}
ALOGV("getToneForRegion, tone %d, region %d, regionTone %d", toneType, mRegion, regionTone);
diff --git a/media/libstagefright/AACWriter.cpp b/media/libstagefright/AACWriter.cpp
index 1673ccd..9cdb463 100644
--- a/media/libstagefright/AACWriter.cpp
+++ b/media/libstagefright/AACWriter.cpp
@@ -60,7 +60,7 @@
AACWriter::~AACWriter() {
if (mStarted) {
- stop();
+ reset();
}
if (mFd != -1) {
@@ -152,7 +152,7 @@
return OK;
}
-status_t AACWriter::stop() {
+status_t AACWriter::reset() {
if (!mStarted) {
return OK;
}
diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp
index 6c4e307..59b4ca7 100644
--- a/media/libstagefright/AMRWriter.cpp
+++ b/media/libstagefright/AMRWriter.cpp
@@ -52,7 +52,7 @@
AMRWriter::~AMRWriter() {
if (mStarted) {
- stop();
+ reset();
}
if (mFd != -1) {
@@ -152,7 +152,7 @@
return OK;
}
-status_t AMRWriter::stop() {
+status_t AMRWriter::reset() {
if (!mStarted) {
return OK;
}
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 03e8a06..483e5ab 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -78,6 +78,7 @@
LOCAL_STATIC_LIBRARIES := \
libstagefright_color_conversion \
+ libstagefright_aacenc \
libstagefright_avcenc \
libstagefright_m4vh263enc \
libstagefright_matroska \
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index 8bdb7c5..fef2a00 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -72,7 +72,7 @@
AudioSource::~AudioSource() {
if (mStarted) {
- stop();
+ reset();
}
delete mRecord;
@@ -130,7 +130,7 @@
}
}
-status_t AudioSource::stop() {
+status_t AudioSource::reset() {
Mutex::Autolock autoLock(mLock);
if (!mStarted) {
return UNKNOWN_ERROR;
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 1850c9c..228659c 100755
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -548,7 +548,7 @@
CameraSource::~CameraSource() {
if (mStarted) {
- stop();
+ reset();
} else if (mInitCheck == OK) {
// Camera is initialized but because start() is never called,
// the lock on Camera is never released(). This makes sure
@@ -632,8 +632,8 @@
mCameraFlags = 0;
}
-status_t CameraSource::stop() {
- ALOGD("stop: E");
+status_t CameraSource::reset() {
+ ALOGD("reset: E");
Mutex::Autolock autoLock(mLock);
mStarted = false;
mFrameAvailableCondition.signal();
@@ -670,7 +670,7 @@
}
CHECK_EQ(mNumFramesReceived, mNumFramesEncoded + mNumFramesDropped);
- ALOGD("stop: X");
+ ALOGD("reset: X");
return OK;
}
diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp
index 263ab50..83d67b9 100644
--- a/media/libstagefright/CameraSourceTimeLapse.cpp
+++ b/media/libstagefright/CameraSourceTimeLapse.cpp
@@ -87,6 +87,10 @@
}
CameraSourceTimeLapse::~CameraSourceTimeLapse() {
+ if (mLastReadBufferCopy) {
+ mLastReadBufferCopy->release();
+ mLastReadBufferCopy = NULL;
+ }
}
void CameraSourceTimeLapse::startQuickReadReturns() {
@@ -204,15 +208,6 @@
}
}
-void CameraSourceTimeLapse::stopCameraRecording() {
- ALOGV("stopCameraRecording");
- CameraSource::stopCameraRecording();
- if (mLastReadBufferCopy) {
- mLastReadBufferCopy->release();
- mLastReadBufferCopy = NULL;
- }
-}
-
sp<IMemory> CameraSourceTimeLapse::createIMemoryCopy(
const sp<IMemory> &source_data) {
diff --git a/media/libstagefright/MPEG2TSWriter.cpp b/media/libstagefright/MPEG2TSWriter.cpp
index 36009ab..0b4ecbe 100644
--- a/media/libstagefright/MPEG2TSWriter.cpp
+++ b/media/libstagefright/MPEG2TSWriter.cpp
@@ -513,7 +513,7 @@
MPEG2TSWriter::~MPEG2TSWriter() {
if (mStarted) {
- stop();
+ reset();
}
mLooper->unregisterHandler(mReflector->id());
@@ -564,7 +564,7 @@
return OK;
}
-status_t MPEG2TSWriter::stop() {
+status_t MPEG2TSWriter::reset() {
CHECK(mStarted);
for (size_t i = 0; i < mSources.size(); ++i) {
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 06dd875..068660b 100755
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -282,7 +282,7 @@
}
MPEG4Writer::~MPEG4Writer() {
- stop();
+ reset();
while (!mTracks.empty()) {
List<Track *>::iterator it = mTracks.begin();
@@ -616,7 +616,7 @@
mStarted = false;
}
-status_t MPEG4Writer::stop() {
+status_t MPEG4Writer::reset() {
if (mInitCheck != OK) {
return OK;
} else {
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index af4aa79..381320b 100755
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "OMXCodec"
#include <utils/Log.h>
+#include "include/AACEncoder.h"
#include "include/AVCEncoder.h"
#include "include/M4vH263Encoder.h"
@@ -68,6 +69,7 @@
#define FACTORY_REF(name) { #name, Make##name },
+FACTORY_CREATE_ENCODER(AACEncoder)
FACTORY_CREATE_ENCODER(AVCEncoder)
FACTORY_CREATE_ENCODER(M4vH263Encoder)
@@ -80,6 +82,7 @@
};
static const FactoryInfo kFactoryInfo[] = {
+ FACTORY_REF(AACEncoder)
FACTORY_REF(AVCEncoder)
FACTORY_REF(M4vH263Encoder)
};
@@ -145,6 +148,7 @@
{ MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.google.amrwb.encoder" },
{ MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.encode" },
{ MEDIA_MIMETYPE_AUDIO_AAC, "OMX.google.aac.encoder" },
+ { MEDIA_MIMETYPE_AUDIO_AAC, "AACEncoder" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.DUCATI1.VIDEO.MPEG4E" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7x30.video.encoder.mpeg4" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.encoder.mpeg4" },
diff --git a/media/libstagefright/colorconversion/ColorConverter.cpp b/media/libstagefright/colorconversion/ColorConverter.cpp
index 5cc3f78..f3ef3de 100644
--- a/media/libstagefright/colorconversion/ColorConverter.cpp
+++ b/media/libstagefright/colorconversion/ColorConverter.cpp
@@ -144,8 +144,8 @@
return ERROR_UNSUPPORTED;
}
- uint32_t *dst_ptr = (uint32_t *)dst.mBits
- + (dst.mCropTop * dst.mWidth + dst.mCropLeft) / 2;
+ uint16_t *dst_ptr = (uint16_t *)dst.mBits
+ + dst.mCropTop * dst.mWidth + dst.mCropLeft;
const uint8_t *src_ptr = (const uint8_t *)src.mBits
+ (src.mCropTop * dst.mWidth + src.mCropLeft) * 2;
@@ -182,11 +182,15 @@
| ((kAdjustedClip[g2] >> 2) << 5)
| (kAdjustedClip[b2] >> 3);
- dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
+ if (x + 1 < src.cropWidth()) {
+ *(uint32_t *)(&dst_ptr[x]) = (rgb2 << 16) | rgb1;
+ } else {
+ dst_ptr[x] = rgb1;
+ }
}
src_ptr += src.mWidth * 2;
- dst_ptr += dst.mWidth / 2;
+ dst_ptr += dst.mWidth;
}
return OK;
@@ -290,15 +294,14 @@
const BitmapParams &src, const BitmapParams &dst) {
uint8_t *kAdjustedClip = initClip();
- if (!((dst.mWidth & 3) == 0
- && (src.mCropLeft & 1) == 0
+ if (!((src.mCropLeft & 1) == 0
&& src.cropWidth() == dst.cropWidth()
&& src.cropHeight() == dst.cropHeight())) {
return ERROR_UNSUPPORTED;
}
- uint32_t *dst_ptr = (uint32_t *)dst.mBits
- + (dst.mCropTop * dst.mWidth + dst.mCropLeft) / 2;
+ uint16_t *dst_ptr = (uint16_t *)dst.mBits
+ + dst.mCropTop * dst.mWidth + dst.mCropLeft;
const uint8_t *src_y =
(const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
@@ -340,7 +343,11 @@
| ((kAdjustedClip[g2] >> 2) << 5)
| (kAdjustedClip[r2] >> 3);
- dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
+ if (x + 1 < src.cropWidth()) {
+ *(uint32_t *)(&dst_ptr[x]) = (rgb2 << 16) | rgb1;
+ } else {
+ dst_ptr[x] = rgb1;
+ }
}
src_y += src.mWidth;
@@ -349,7 +356,7 @@
src_u += src.mWidth;
}
- dst_ptr += dst.mWidth / 2;
+ dst_ptr += dst.mWidth;
}
return OK;
@@ -361,15 +368,14 @@
uint8_t *kAdjustedClip = initClip();
- if (!((dst.mWidth & 3) == 0
- && (src.mCropLeft & 1) == 0
+ if (!((src.mCropLeft & 1) == 0
&& src.cropWidth() == dst.cropWidth()
&& src.cropHeight() == dst.cropHeight())) {
return ERROR_UNSUPPORTED;
}
- uint32_t *dst_ptr = (uint32_t *)dst.mBits
- + (dst.mCropTop * dst.mWidth + dst.mCropLeft) / 2;
+ uint16_t *dst_ptr = (uint16_t *)dst.mBits
+ + dst.mCropTop * dst.mWidth + dst.mCropLeft;
const uint8_t *src_y =
(const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
@@ -411,7 +417,11 @@
| ((kAdjustedClip[g2] >> 2) << 5)
| (kAdjustedClip[r2] >> 3);
- dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
+ if (x + 1 < src.cropWidth()) {
+ *(uint32_t *)(&dst_ptr[x]) = (rgb2 << 16) | rgb1;
+ } else {
+ dst_ptr[x] = rgb1;
+ }
}
src_y += src.mWidth;
@@ -420,7 +430,7 @@
src_u += src.mWidth;
}
- dst_ptr += dst.mWidth / 2;
+ dst_ptr += dst.mWidth;
}
return OK;
@@ -430,15 +440,14 @@
const BitmapParams &src, const BitmapParams &dst) {
uint8_t *kAdjustedClip = initClip();
- if (!((dst.mWidth & 3) == 0
- && (src.mCropLeft & 1) == 0
+ if (!((src.mCropLeft & 1) == 0
&& src.cropWidth() == dst.cropWidth()
&& src.cropHeight() == dst.cropHeight())) {
return ERROR_UNSUPPORTED;
}
- uint32_t *dst_ptr = (uint32_t *)dst.mBits
- + (dst.mCropTop * dst.mWidth + dst.mCropLeft) / 2;
+ uint16_t *dst_ptr = (uint16_t *)dst.mBits
+ + dst.mCropTop * dst.mWidth + dst.mCropLeft;
const uint8_t *src_y = (const uint8_t *)src.mBits;
@@ -478,7 +487,11 @@
| ((kAdjustedClip[g2] >> 2) << 5)
| (kAdjustedClip[b2] >> 3);
- dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
+ if (x + 1 < src.cropWidth()) {
+ *(uint32_t *)(&dst_ptr[x]) = (rgb2 << 16) | rgb1;
+ } else {
+ dst_ptr[x] = rgb1;
+ }
}
src_y += src.mWidth;
@@ -487,7 +500,7 @@
src_u += src.mWidth;
}
- dst_ptr += dst.mWidth / 2;
+ dst_ptr += dst.mWidth;
}
return OK;
diff --git a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
index 0914f32..c79e01f 100644
--- a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
+++ b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
@@ -333,8 +333,9 @@
void SimpleSoftOMXComponent::onMessageReceived(const sp<AMessage> &msg) {
Mutex::Autolock autoLock(mLock);
-
- switch (msg->what()) {
+ uint32_t msgType = msg->what();
+ ALOGV("msgType = %d", msgType);
+ switch (msgType) {
case kWhatSendCommand:
{
int32_t cmd, param;
@@ -354,27 +355,27 @@
CHECK(mState == OMX_StateExecuting && mTargetState == mState);
bool found = false;
- for (size_t i = 0; i < mPorts.size(); ++i) {
- PortInfo *port = &mPorts.editItemAt(i);
+ size_t portIndex = (kWhatEmptyThisBuffer == msgType)?
+ header->nInputPortIndex: header->nOutputPortIndex;
+ PortInfo *port = &mPorts.editItemAt(portIndex);
- for (size_t j = 0; j < port->mBuffers.size(); ++j) {
- BufferInfo *buffer = &port->mBuffers.editItemAt(j);
+ for (size_t j = 0; j < port->mBuffers.size(); ++j) {
+ BufferInfo *buffer = &port->mBuffers.editItemAt(j);
- if (buffer->mHeader == header) {
- CHECK(!buffer->mOwnedByUs);
+ if (buffer->mHeader == header) {
+ CHECK(!buffer->mOwnedByUs);
- buffer->mOwnedByUs = true;
+ buffer->mOwnedByUs = true;
- CHECK((msg->what() == kWhatEmptyThisBuffer
- && port->mDef.eDir == OMX_DirInput)
- || (port->mDef.eDir == OMX_DirOutput));
+ CHECK((msgType == kWhatEmptyThisBuffer
+ && port->mDef.eDir == OMX_DirInput)
+ || (port->mDef.eDir == OMX_DirOutput));
- port->mQueue.push_back(buffer);
- onQueueFilled(i);
+ port->mQueue.push_back(buffer);
+ onQueueFilled(portIndex);
- found = true;
- break;
- }
+ found = true;
+ break;
}
}
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 5cf5236..6b2ae51 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -345,42 +345,73 @@
void egl_display_t::loseCurrent(egl_context_t * cur_c)
{
if (cur_c) {
- egl_surface_t * cur_r = get_surface(cur_c->read);
- egl_surface_t * cur_d = get_surface(cur_c->draw);
+ egl_display_t* display = cur_c->getDisplay();
+ if (display) {
+ display->loseCurrentImpl(cur_c);
+ }
+ }
+}
- // by construction, these are either 0 or valid (possibly terminated)
- // it should be impossible for these to be invalid
- ContextRef _cur_c(cur_c);
- SurfaceRef _cur_r(cur_r);
- SurfaceRef _cur_d(cur_d);
+void egl_display_t::loseCurrentImpl(egl_context_t * cur_c)
+{
+ // by construction, these are either 0 or valid (possibly terminated)
+ // it should be impossible for these to be invalid
+ ContextRef _cur_c(cur_c);
+ SurfaceRef _cur_r(cur_c ? get_surface(cur_c->read) : NULL);
+ SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : NULL);
+ { // scope for the lock
+ Mutex::Autolock _l(lock);
cur_c->onLooseCurrent();
- _cur_c.release();
- _cur_r.release();
- _cur_d.release();
}
+
+ // This cannot be called with the lock held because it might end-up
+ // calling back into EGL (in particular when a surface is destroyed
+ // it calls ANativeWindow::disconnect
+ _cur_c.release();
+ _cur_r.release();
+ _cur_d.release();
}
EGLBoolean egl_display_t::makeCurrent(egl_context_t* c, egl_context_t* cur_c,
EGLSurface draw, EGLSurface read, EGLContext ctx,
EGLSurface impl_draw, EGLSurface impl_read, EGLContext impl_ctx)
{
- Mutex::Autolock _l(lock);
EGLBoolean result;
- if (c) {
- result = c->cnx->egl.eglMakeCurrent(
- disp[c->impl].dpy, impl_draw, impl_read, impl_ctx);
- } else {
- result = cur_c->cnx->egl.eglMakeCurrent(
- disp[cur_c->impl].dpy, impl_draw, impl_read, impl_ctx);
- }
- if (result == EGL_TRUE) {
- loseCurrent(cur_c);
+
+ // by construction, these are either 0 or valid (possibly terminated)
+ // it should be impossible for these to be invalid
+ ContextRef _cur_c(cur_c);
+ SurfaceRef _cur_r(cur_c ? get_surface(cur_c->read) : NULL);
+ SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : NULL);
+
+ { // scope for the lock
+ Mutex::Autolock _l(lock);
if (c) {
- c->onMakeCurrent(draw, read);
+ result = c->cnx->egl.eglMakeCurrent(
+ disp[c->impl].dpy, impl_draw, impl_read, impl_ctx);
+ if (result == EGL_TRUE) {
+ c->onMakeCurrent(draw, read);
+ }
+ } else {
+ result = cur_c->cnx->egl.eglMakeCurrent(
+ disp[cur_c->impl].dpy, impl_draw, impl_read, impl_ctx);
+ if (result == EGL_TRUE) {
+ cur_c->onLooseCurrent();
+ }
}
}
+
+ if (result == EGL_TRUE) {
+ // This cannot be called with the lock held because it might end-up
+ // calling back into EGL (in particular when a surface is destroyed
+ // it calls ANativeWindow::disconnect
+ _cur_c.release();
+ _cur_r.release();
+ _cur_d.release();
+ }
+
return result;
}
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index 4479e00..f3c4ddf 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -64,6 +64,7 @@
class EGLAPI egl_display_t { // marked as EGLAPI for testing purposes
static egl_display_t sDisplay[NUM_DISPLAYS];
EGLDisplay getDisplay(EGLNativeDisplayType display);
+ void loseCurrentImpl(egl_context_t * cur_c);
public:
enum {
diff --git a/packages/SystemUI/src/com/android/systemui/net/NetworkOverLimitActivity.java b/packages/SystemUI/src/com/android/systemui/net/NetworkOverLimitActivity.java
index 723e338..888b76e 100644
--- a/packages/SystemUI/src/com/android/systemui/net/NetworkOverLimitActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/net/NetworkOverLimitActivity.java
@@ -77,7 +77,7 @@
final INetworkPolicyManager policyService = INetworkPolicyManager.Stub.asInterface(
ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
try {
- policyService.snoozePolicy(template);
+ policyService.snoozeLimit(template);
} catch (RemoteException e) {
Slog.w(TAG, "problem snoozing network policy", e);
}
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index 38c85bb..bcba3c2 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -16,16 +16,23 @@
package com.android.internal.policy.impl;
-import android.app.Activity;
+import com.android.internal.app.ShutdownThread;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.TelephonyProperties;
+import com.android.internal.R;
+
+import android.app.ActivityManagerNative;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.UserInfo;
import android.media.AudioManager;
import android.os.Handler;
import android.os.Message;
+import android.os.RemoteException;
import android.os.SystemProperties;
import android.provider.Settings;
import android.telephony.PhoneStateListener;
@@ -39,13 +46,9 @@
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
-import com.android.internal.R;
-import com.android.internal.app.ShutdownThread;
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.TelephonyProperties;
-import com.google.android.collect.Lists;
import java.util.ArrayList;
+import java.util.List;
/**
* Helper to show the global actions dialog. Each item is an {@link Action} that
@@ -101,9 +104,10 @@
public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
mKeyguardShowing = keyguardShowing;
mDeviceProvisioned = isDeviceProvisioned;
- if (mDialog == null) {
- mDialog = createDialog();
+ if (mDialog != null) {
+ mDialog.dismiss();
}
+ mDialog = createDialog();
prepareDialog();
mDialog.show();
@@ -187,6 +191,31 @@
mItems.add(mSilentModeAction);
}
+ List<UserInfo> users = mContext.getPackageManager().getUsers();
+ if (users.size() > 1) {
+ for (final UserInfo user : users) {
+ SinglePressAction switchToUser = new SinglePressAction(
+ com.android.internal.R.drawable.ic_menu_cc,
+ user.name != null ? user.name : "Primary") {
+ public void onPress() {
+ try {
+ ActivityManagerNative.getDefault().switchUser(user.id);
+ } catch (RemoteException re) {
+ Log.e(TAG, "Couldn't switch user " + re);
+ }
+ }
+
+ public boolean showDuringKeyguard() {
+ return true;
+ }
+
+ public boolean showBeforeProvisioning() {
+ return false;
+ }
+ };
+ mItems.add(switchToUser);
+ }
+ }
mAdapter = new MyAdapter();
final AlertDialog.Builder ab = new AlertDialog.Builder(mContext);
@@ -341,12 +370,19 @@
private static abstract class SinglePressAction implements Action {
private final int mIconResId;
private final int mMessageResId;
+ private final CharSequence mMessage;
protected SinglePressAction(int iconResId, int messageResId) {
mIconResId = iconResId;
mMessageResId = messageResId;
+ mMessage = null;
}
+ protected SinglePressAction(int iconResId, CharSequence message) {
+ mIconResId = iconResId;
+ mMessageResId = 0;
+ mMessage = message;
+ }
public boolean isEnabled() {
return true;
}
@@ -363,7 +399,11 @@
v.findViewById(R.id.status).setVisibility(View.GONE);
icon.setImageDrawable(context.getResources().getDrawable(mIconResId));
- messageView.setText(mMessageResId);
+ if (mMessage != null) {
+ messageView.setText(mMessage);
+ } else {
+ messageView.setText(mMessageResId);
+ }
return v;
}
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 2d856ad..f71ba0a 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -160,7 +160,10 @@
AudioFlinger::AudioFlinger()
: BnAudioFlinger(),
- mPrimaryHardwareDev(NULL), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1),
+ mPrimaryHardwareDev(NULL),
+ mHardwareStatus(AUDIO_HW_IDLE), // see also onFirstRef()
+ mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1),
+ mMode(AUDIO_MODE_INVALID),
mBtNrecIsOff(false)
{
}
@@ -172,7 +175,6 @@
Mutex::Autolock _l(mLock);
/* TODO: move all this work into an Init() function */
- mHardwareStatus = AUDIO_HW_IDLE;
for (size_t i = 0; i < ARRAY_SIZE(audio_interfaces); i++) {
const hw_module_t *mod;
@@ -265,13 +267,10 @@
result.append("Clients:\n");
for (size_t i = 0; i < mClients.size(); ++i) {
- wp<Client> wClient = mClients.valueAt(i);
- if (wClient != 0) {
- sp<Client> client = wClient.promote();
- if (client != 0) {
- snprintf(buffer, SIZE, " pid: %d\n", client->pid());
- result.append(buffer);
- }
+ sp<Client> client = mClients.valueAt(i).promote();
+ if (client != 0) {
+ snprintf(buffer, SIZE, " pid: %d\n", client->pid());
+ result.append(buffer);
}
}
@@ -971,7 +970,8 @@
{
size_t size = mNotificationClients.size();
for (size_t i = 0; i < size; i++) {
- mNotificationClients.valueAt(i)->client()->ioConfigChanged(event, ioHandle, param2);
+ mNotificationClients.valueAt(i)->audioFlingerClient()->ioConfigChanged(event, ioHandle,
+ param2);
}
}
@@ -985,13 +985,19 @@
// ----------------------------------------------------------------------------
-AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, int id, uint32_t device)
+AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, int id, uint32_t device,
+ type_t type)
: Thread(false),
- mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0), mChannelCount(0),
- mFrameSize(1), mFormat(AUDIO_FORMAT_INVALID), mStandby(false), mId(id), mExiting(false),
- mDevice(device)
+ mType(type),
+ mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0),
+ // mChannelMask
+ mChannelCount(0),
+ mFrameSize(1), mFormat(AUDIO_FORMAT_INVALID),
+ mParamStatus(NO_ERROR),
+ mStandby(false), mId(id), mExiting(false),
+ mDevice(device),
+ mDeathRecipient(new PMDeathRecipient(this))
{
- mDeathRecipient = new PMDeathRecipient(this);
}
AudioFlinger::ThreadBase::~ThreadBase()
@@ -1372,20 +1378,24 @@
AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger,
AudioStreamOut* output,
int id,
- uint32_t device)
- : ThreadBase(audioFlinger, id, device),
- mMixBuffer(NULL), mSuspended(0), mBytesWritten(0), mOutput(output),
+ uint32_t device,
+ type_t type)
+ : ThreadBase(audioFlinger, id, device, type),
+ mMixBuffer(NULL), mSuspended(0), mBytesWritten(0),
+ // Assumes constructor is called by AudioFlinger with it's mLock held,
+ // but it would be safer to explicitly pass initial masterMute as parameter
+ mMasterMute(audioFlinger->masterMute_l()),
+ // mStreamTypes[] initialized in constructor body
+ mOutput(output),
+ // Assumes constructor is called by AudioFlinger with it's mLock held,
+ // but it would be safer to explicitly pass initial masterVolume as parameter
+ mMasterVolume(audioFlinger->masterVolume_l()),
mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false)
{
snprintf(mName, kNameLength, "AudioOut_%d", id);
readOutputParameters();
- // Assumes constructor is called by AudioFlinger with it's mLock held,
- // but it would be safer to explicitly pass these as parameters
- mMasterVolume = mAudioFlinger->masterVolume_l();
- mMasterMute = mAudioFlinger->masterMute_l();
-
// mStreamTypes[AUDIO_STREAM_CNT] is initialized by stream_type_t default constructor
// There is no AUDIO_STREAM_MIN, and ++ operator does not compile
for (audio_stream_type_t stream = (audio_stream_type_t) 0; stream < AUDIO_STREAM_CNT;
@@ -1431,13 +1441,10 @@
result.append(buffer);
result.append(" Name Clien Typ Fmt Chn mask Session Buf S M F SRate LeftV RighV Serv User Main buf Aux Buf\n");
for (size_t i = 0; i < mActiveTracks.size(); ++i) {
- wp<Track> wTrack = mActiveTracks[i];
- if (wTrack != 0) {
- sp<Track> track = wTrack.promote();
- if (track != 0) {
- track->dump(buffer, SIZE);
- result.append(buffer);
- }
+ sp<Track> track = mActiveTracks[i].promote();
+ if (track != 0) {
+ track->dump(buffer, SIZE);
+ result.append(buffer);
}
}
write(fd, result.string(), result.size());
@@ -1705,7 +1712,7 @@
// audioConfigChanged_l() must be called with AudioFlinger::mLock held
void AudioFlinger::PlaybackThread::audioConfigChanged_l(int event, int param) {
AudioSystem::OutputDescriptor desc;
- void *param2 = 0;
+ void *param2 = NULL;
ALOGV("PlaybackThread::audioConfigChanged_l, thread %p, event %d, param %d", this, event, param);
@@ -1740,7 +1747,7 @@
// FIXME - Current mixer implementation only supports stereo output: Always
// Allocate a stereo buffer even if HW output is mono.
- if (mMixBuffer != NULL) delete[] mMixBuffer;
+ delete[] mMixBuffer;
mMixBuffer = new int16_t[mFrameCount * 2];
memset(mMixBuffer, 0, mFrameCount * 2 * sizeof(int16_t));
@@ -1758,7 +1765,7 @@
status_t AudioFlinger::PlaybackThread::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames)
{
- if (halFrames == 0 || dspFrames == 0) {
+ if (halFrames == NULL || dspFrames == NULL) {
return BAD_VALUE;
}
Mutex::Autolock _l(mLock);
@@ -1845,13 +1852,12 @@
// ----------------------------------------------------------------------------
-AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device)
- : PlaybackThread(audioFlinger, output, id, device),
- mAudioMixer(NULL), mPrevMixerStatus(MIXER_IDLE)
+AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
+ int id, uint32_t device, type_t type)
+ : PlaybackThread(audioFlinger, output, id, device, type),
+ mAudioMixer(new AudioMixer(mFrameCount, mSampleRate)),
+ mPrevMixerStatus(MIXER_IDLE)
{
- mType = ThreadBase::MIXER;
- mAudioMixer = new AudioMixer(mFrameCount, mSampleRate);
-
// FIXME - Current mixer implementation only supports stereo output
if (mChannelCount == 1) {
ALOGE("Invalid audio hardware channel count");
@@ -2193,7 +2199,7 @@
// read original volumes with volume control
float typeVolume = mStreamTypes[track->type()].volume;
float v = masterVolume * typeVolume;
- uint32_t vlr = cblk->volumeLR;
+ uint32_t vlr = cblk->getVolumeLR();
vl = vlr & 0xFFFF;
vr = vlr >> 16;
// track volumes come from shared memory, so can't be trusted and must be clamped
@@ -2461,6 +2467,8 @@
}
if (status == NO_ERROR && reconfig) {
delete mAudioMixer;
+ // for safety in case readOutputParameters() accesses mAudioMixer (it doesn't)
+ mAudioMixer = NULL;
readOutputParameters();
mAudioMixer = new AudioMixer(mFrameCount, mSampleRate);
for (size_t i = 0; i < mTracks.size() ; i++) {
@@ -2513,9 +2521,10 @@
// ----------------------------------------------------------------------------
AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device)
- : PlaybackThread(audioFlinger, output, id, device)
+ : PlaybackThread(audioFlinger, output, id, device, DIRECT)
+ // mLeftVolFloat, mRightVolFloat
+ // mLeftVolShort, mRightVolShort
{
- mType = ThreadBase::DIRECT;
}
AudioFlinger::DirectOutputThread::~DirectOutputThread()
@@ -2729,7 +2738,7 @@
} else {
float typeVolume = mStreamTypes[track->type()].volume;
float v = mMasterVolume * typeVolume;
- uint32_t vlr = cblk->volumeLR;
+ uint32_t vlr = cblk->getVolumeLR();
float v_clamped = v * (vlr & 0xFFFF);
if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
left = v_clamped/MAX_GAIN;
@@ -2992,10 +3001,11 @@
// ----------------------------------------------------------------------------
-AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger, AudioFlinger::MixerThread* mainThread, int id)
- : MixerThread(audioFlinger, mainThread->getOutput(), id, mainThread->device()), mWaitTimeMs(UINT_MAX)
+AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger,
+ AudioFlinger::MixerThread* mainThread, int id)
+ : MixerThread(audioFlinger, mainThread->getOutput(), id, mainThread->device(), DUPLICATING),
+ mWaitTimeMs(UINT_MAX)
{
- mType = ThreadBase::DUPLICATING;
addOutputTrack(mainThread);
}
@@ -3244,13 +3254,17 @@
: RefBase(),
mThread(thread),
mClient(client),
- mCblk(0),
+ mCblk(NULL),
+ // mBuffer
+ // mBufferEnd
mFrameCount(0),
mState(IDLE),
mClientTid(-1),
mFormat(format),
mFlags(flags & ~SYSTEM_FLAGS_MASK),
mSessionId(sessionId)
+ // mChannelCount
+ // mChannelMask
{
ALOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size());
@@ -3266,7 +3280,7 @@
mCblkMemory = client->heap()->allocate(size);
if (mCblkMemory != 0) {
mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer());
- if (mCblk) { // construct the shared structure in-place.
+ if (mCblk != NULL) { // construct the shared structure in-place.
new(mCblk) audio_track_cblk_t();
// clear all buffers
mCblk->frameCount = frameCount;
@@ -3309,7 +3323,7 @@
AudioFlinger::ThreadBase::TrackBase::~TrackBase()
{
- if (mCblk) {
+ if (mCblk != NULL) {
mCblk->~audio_track_cblk_t(); // destroy our shared-structure.
if (mClient == NULL) {
delete mCblk;
@@ -3317,6 +3331,7 @@
}
mCblkMemory.clear(); // and free the shared memory
if (mClient != NULL) {
+ // Client destructor must run with AudioFlinger mutex locked
Mutex::Autolock _l(mClient->audioFlinger()->mLock);
mClient.clear();
}
@@ -3383,7 +3398,7 @@
server %d, serverBase %d, user %d, userBase %d",
bufferStart, bufferEnd, mBuffer, mBufferEnd,
cblk->server, cblk->serverBase, cblk->user, cblk->userBase);
- return 0;
+ return NULL;
}
return bufferStart;
@@ -3468,7 +3483,7 @@
void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
{
- uint32_t vlr = mCblk->volumeLR;
+ uint32_t vlr = mCblk->getVolumeLR();
snprintf(buffer, size, " %05d %05d %03u %03u 0x%08x %05u %04u %1d %1d %1d %05u %05u %05u 0x%08x 0x%08x 0x%08x 0x%08x\n",
mName - AudioMixer::TRACK0,
(mClient == NULL) ? getpid() : mClient->pid(),
@@ -3827,7 +3842,6 @@
if (mCblk != NULL) {
mCblk->flags |= CBLK_DIRECTION_OUT;
mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);
- mCblk->volumeLR = (MAX_GAIN_INT << 16) | MAX_GAIN_INT;
mOutBuffer.frameCount = 0;
playbackThread->mTracks.add(this);
ALOGV("OutputTrack constructor mCblk %p, mBuffer %p, mCblk->buffers %p, " \
@@ -4065,7 +4079,7 @@
mAudioFlinger->removeClient_l(mPid);
}
-const sp<MemoryDealer>& AudioFlinger::Client::heap() const
+sp<MemoryDealer> AudioFlinger::Client::heap() const
{
return mMemoryDealer;
}
@@ -4075,13 +4089,12 @@
AudioFlinger::NotificationClient::NotificationClient(const sp<AudioFlinger>& audioFlinger,
const sp<IAudioFlingerClient>& client,
pid_t pid)
- : mAudioFlinger(audioFlinger), mPid(pid), mClient(client)
+ : mAudioFlinger(audioFlinger), mPid(pid), mAudioFlingerClient(client)
{
}
AudioFlinger::NotificationClient::~NotificationClient()
{
- mClient.clear();
}
void AudioFlinger::NotificationClient::binderDied(const wp<IBinder>& who)
@@ -4266,15 +4279,16 @@
uint32_t channels,
int id,
uint32_t device) :
- ThreadBase(audioFlinger, id, device),
- mInput(input), mTrack(NULL), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL)
+ ThreadBase(audioFlinger, id, device, RECORD),
+ mInput(input), mTrack(NULL), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL),
+ // mRsmpInIndex and mInputBytes set by readInputParameters()
+ mReqChannelCount(popcount(channels)),
+ mReqSampleRate(sampleRate)
+ // mBytesRead is only meaningful while active, and so is cleared in start()
+ // (but might be better to also clear here for dump?)
{
- mType = ThreadBase::RECORD;
-
snprintf(mName, kNameLength, "AudioIn_%d", id);
- mReqChannelCount = popcount(channels);
- mReqSampleRate = sampleRate;
readInputParameters();
}
@@ -4282,10 +4296,8 @@
AudioFlinger::RecordThread::~RecordThread()
{
delete[] mRsmpInBuffer;
- if (mResampler != NULL) {
- delete mResampler;
- delete[] mRsmpOutBuffer;
- }
+ delete mResampler;
+ delete[] mRsmpOutBuffer;
}
void AudioFlinger::RecordThread::onFirstRef()
@@ -4807,7 +4819,7 @@
void AudioFlinger::RecordThread::audioConfigChanged_l(int event, int param) {
AudioSystem::OutputDescriptor desc;
- void *param2 = 0;
+ void *param2 = NULL;
switch (event) {
case AudioSystem::INPUT_OPENED:
@@ -4829,9 +4841,11 @@
void AudioFlinger::RecordThread::readInputParameters()
{
- if (mRsmpInBuffer) delete mRsmpInBuffer;
- if (mRsmpOutBuffer) delete mRsmpOutBuffer;
- if (mResampler) delete mResampler;
+ delete mRsmpInBuffer;
+ // mRsmpInBuffer is always assigned a new[] below
+ delete mRsmpOutBuffer;
+ mRsmpOutBuffer = NULL;
+ delete mResampler;
mResampler = NULL;
mSampleRate = mInput->stream->common.get_sample_rate(&mInput->stream->common);
@@ -4983,10 +4997,10 @@
}
mPlaybackThreads.add(id, thread);
- if (pSamplingRate) *pSamplingRate = samplingRate;
- if (pFormat) *pFormat = format;
- if (pChannels) *pChannels = channels;
- if (pLatencyMs) *pLatencyMs = thread->latency();
+ if (pSamplingRate != NULL) *pSamplingRate = samplingRate;
+ if (pFormat != NULL) *pFormat = format;
+ if (pChannels != NULL) *pChannels = channels;
+ if (pLatencyMs != NULL) *pLatencyMs = thread->latency();
// notify client processes of the new output creation
thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED);
@@ -5038,7 +5052,7 @@
}
}
}
- void *param2 = 0;
+ void *param2 = NULL;
audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, output, param2);
mPlaybackThreads.removeItem(output);
}
@@ -5089,7 +5103,7 @@
uint32_t *pSamplingRate,
audio_format_t *pFormat,
uint32_t *pChannels,
- uint32_t acoustics)
+ audio_in_acoustics_t acoustics)
{
status_t status;
RecordThread *thread = NULL;
@@ -5114,7 +5128,7 @@
status = inHwDev->open_input_stream(inHwDev, *pDevices, &format,
&channels, &samplingRate,
- (audio_in_acoustics_t)acoustics,
+ acoustics,
&inStream);
ALOGV("openInput() openInputStream returned input %p, SamplingRate %d, Format %d, Channels %x, acoustics %x, status %d",
inStream,
@@ -5134,7 +5148,7 @@
ALOGV("openInput() reopening with proposed sampling rate and channels");
status = inHwDev->open_input_stream(inHwDev, *pDevices, &format,
&channels, &samplingRate,
- (audio_in_acoustics_t)acoustics,
+ acoustics,
&inStream);
}
@@ -5154,9 +5168,9 @@
device);
mRecordThreads.add(id, thread);
ALOGV("openInput() created record thread: ID %d thread %p", id, thread);
- if (pSamplingRate) *pSamplingRate = reqSamplingRate;
- if (pFormat) *pFormat = format;
- if (pChannels) *pChannels = reqChannels;
+ if (pSamplingRate != NULL) *pSamplingRate = reqSamplingRate;
+ if (pFormat != NULL) *pFormat = format;
+ if (pChannels != NULL) *pChannels = reqChannels;
input->stream->common.standby(&input->stream->common);
@@ -5181,7 +5195,7 @@
}
ALOGV("closeInput() %d", input);
- void *param2 = 0;
+ void *param2 = NULL;
audioConfigChanged_l(AudioSystem::INPUT_CLOSED, input, param2);
mRecordThreads.removeItem(input);
}
@@ -5243,12 +5257,8 @@
return;
}
}
- AudioSessionRef *ref = new AudioSessionRef();
- ref->sessionid = audioSession;
- ref->pid = caller;
- ref->cnt = 1;
- mAudioSessionRefs.push(ref);
- ALOGV(" added new entry for %d", ref->sessionid);
+ mAudioSessionRefs.push(new AudioSessionRef(audioSession, caller));
+ ALOGV(" added new entry for %d", audioSession);
}
void AudioFlinger::releaseAudioSessionId(int audioSession)
@@ -5792,7 +5802,7 @@
// create effect handle and connect it to effect module
handle = new EffectHandle(effect, client, effectClient, priority);
lStatus = effect->addHandle(handle);
- if (enabled) {
+ if (enabled != NULL) {
*enabled = (int)effect->isEnabled();
}
}
@@ -6179,7 +6189,7 @@
}
}
-status_t AudioFlinger::EffectModule::addHandle(sp<EffectHandle>& handle)
+status_t AudioFlinger::EffectModule::addHandle(const sp<EffectHandle>& handle)
{
status_t status;
@@ -6226,7 +6236,7 @@
bool enabled = false;
EffectHandle *hdl = handle.unsafe_get();
- if (hdl) {
+ if (hdl != NULL) {
ALOGV("removeHandle() unsafe_get OK");
enabled = hdl->enabled();
}
@@ -6862,7 +6872,7 @@
if (mCblkMemory != 0) {
mCblk = static_cast<effect_param_cblk_t *>(mCblkMemory->pointer());
- if (mCblk) {
+ if (mCblk != NULL) {
new(mCblk) effect_param_cblk_t();
mBuffer = (uint8_t *)mCblk + bufOffset;
}
@@ -6959,7 +6969,7 @@
// release sp on module => module destructor can be called now
mEffect.clear();
if (mClient != 0) {
- if (mCblk) {
+ if (mCblk != NULL) {
mCblk->~effect_param_cblk_t(); // destroy our shared-structure.
}
mCblkMemory.clear(); // and free the shared memory
@@ -7089,7 +7099,7 @@
void AudioFlinger::EffectHandle::dump(char* buffer, size_t size)
{
- bool locked = mCblk ? tryLock(mCblk->lock) : false;
+ bool locked = mCblk != NULL && tryLock(mCblk->lock);
snprintf(buffer, size, "\t\t\t%05d %05d %01u %01u %05u %05u\n",
(mClient == NULL) ? getpid() : mClient->pid(),
@@ -7551,7 +7561,8 @@
ALOGV("setEffectSuspendedAll_l() add entry for 0");
}
if (desc->mRefCount++ == 0) {
- Vector< sp<EffectModule> > effects = getSuspendEligibleEffects();
+ Vector< sp<EffectModule> > effects;
+ getSuspendEligibleEffects(effects);
for (size_t i = 0; i < effects.size(); i++) {
setEffectSuspended_l(&effects[i]->desc().type, true);
}
@@ -7602,16 +7613,14 @@
return true;
}
-Vector< sp<AudioFlinger::EffectModule> > AudioFlinger::EffectChain::getSuspendEligibleEffects()
+void AudioFlinger::EffectChain::getSuspendEligibleEffects(Vector< sp<AudioFlinger::EffectModule> > &effects)
{
- Vector< sp<EffectModule> > effects;
+ effects.clear();
for (size_t i = 0; i < mEffects.size(); i++) {
- if (!isEffectEligibleForSuspend(mEffects[i]->desc())) {
- continue;
+ if (isEffectEligibleForSuspend(mEffects[i]->desc())) {
+ effects.add(mEffects[i]);
}
- effects.add(mEffects[i]);
}
- return effects;
}
sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectIfEnabled(
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 766ba44..3f3188c 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -131,7 +131,7 @@
uint32_t *pSamplingRate,
audio_format_t *pFormat,
uint32_t *pChannels,
- uint32_t acoustics);
+ audio_in_acoustics_t acoustics);
virtual status_t closeInput(int input);
@@ -226,16 +226,16 @@
public:
Client(const sp<AudioFlinger>& audioFlinger, pid_t pid);
virtual ~Client();
- const sp<MemoryDealer>& heap() const;
+ sp<MemoryDealer> heap() const;
pid_t pid() const { return mPid; }
sp<AudioFlinger> audioFlinger() { return mAudioFlinger; }
private:
Client(const Client&);
Client& operator = (const Client&);
- sp<AudioFlinger> mAudioFlinger;
- sp<MemoryDealer> mMemoryDealer;
- pid_t mPid;
+ const sp<AudioFlinger> mAudioFlinger;
+ const sp<MemoryDealer> mMemoryDealer;
+ const pid_t mPid;
};
// --- Notification Client ---
@@ -246,7 +246,7 @@
pid_t pid);
virtual ~NotificationClient();
- sp<IAudioFlingerClient> client() { return mClient; }
+ sp<IAudioFlingerClient> audioFlingerClient() const { return mAudioFlingerClient; }
// IBinder::DeathRecipient
virtual void binderDied(const wp<IBinder>& who);
@@ -255,9 +255,9 @@
NotificationClient(const NotificationClient&);
NotificationClient& operator = (const NotificationClient&);
- sp<AudioFlinger> mAudioFlinger;
- pid_t mPid;
- sp<IAudioFlingerClient> mClient;
+ const sp<AudioFlinger> mAudioFlinger;
+ const pid_t mPid;
+ const sp<IAudioFlingerClient> mAudioFlingerClient;
};
class TrackHandle;
@@ -277,17 +277,17 @@
class ThreadBase : public Thread {
public:
- ThreadBase (const sp<AudioFlinger>& audioFlinger, int id, uint32_t device);
- virtual ~ThreadBase();
-
- enum type {
+ enum type_t {
MIXER, // Thread class is MixerThread
DIRECT, // Thread class is DirectOutputThread
DUPLICATING, // Thread class is DuplicatingThread
RECORD // Thread class is RecordThread
};
+ ThreadBase (const sp<AudioFlinger>& audioFlinger, int id, uint32_t device, type_t type);
+ virtual ~ThreadBase();
+
status_t dumpBase(int fd, const Vector<String16>& args);
status_t dumpEffectChains(int fd, const Vector<String16>& args);
@@ -367,8 +367,8 @@
bool step();
void reset();
- wp<ThreadBase> mThread;
- sp<Client> mClient;
+ const wp<ThreadBase> mThread;
+ /*const*/ sp<Client> mClient; // see explanation at ~TrackBase() why not const
sp<IMemory> mCblkMemory;
audio_track_cblk_t* mCblk;
void* mBuffer;
@@ -377,9 +377,9 @@
// we don't really need a lock for these
track_state mState;
int mClientTid;
- audio_format_t mFormat;
+ const audio_format_t mFormat;
uint32_t mFlags;
- int mSessionId;
+ const int mSessionId;
uint8_t mChannelCount;
uint32_t mChannelMask;
};
@@ -408,7 +408,7 @@
};
virtual status_t initCheck() const = 0;
- int type() const { return mType; }
+ type_t type() const { return mType; }
uint32_t sampleRate() const;
int channelCount() const;
audio_format_t format() const;
@@ -530,9 +530,9 @@
friend class RecordThread;
friend class RecordTrack;
- int mType;
+ const type_t mType;
Condition mWaitWorkCV;
- sp<AudioFlinger> mAudioFlinger;
+ const sp<AudioFlinger> mAudioFlinger;
uint32_t mSampleRate;
size_t mFrameCount;
uint32_t mChannelMask;
@@ -553,7 +553,7 @@
char mName[kNameLength];
sp<IPowerManager> mPowerManager;
sp<IBinder> mWakeLockToken;
- sp<PMDeathRecipient> mDeathRecipient;
+ const sp<PMDeathRecipient> mDeathRecipient;
// list of suspended effects per session and per type. The first vector is
// keyed by session ID, the second by type UUID timeLow field
KeyedVector< int, KeyedVector< int, sp<SuspendedSessionDesc> > > mSuspendedSessions;
@@ -671,7 +671,7 @@
bool write(int16_t* data, uint32_t frames);
bool bufferQueueEmpty() { return (mBufferQueue.size() == 0) ? true : false; }
bool isActive() { return mActive; }
- wp<ThreadBase>& thread() { return mThread; }
+ const wp<ThreadBase>& thread() { return mThread; }
private:
@@ -688,10 +688,11 @@
Vector < Buffer* > mBufferQueue;
AudioBufferProvider::Buffer mOutBuffer;
bool mActive;
- DuplicatingThread* mSourceThread;
+ DuplicatingThread* const mSourceThread; // for waitTimeMs() in write()
}; // end of OutputTrack
- PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device);
+ PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id,
+ uint32_t device, type_t type);
virtual ~PlaybackThread();
virtual status_t dump(int fd, const Vector<String16>& args);
@@ -817,7 +818,8 @@
MixerThread (const sp<AudioFlinger>& audioFlinger,
AudioStreamOut* output,
int id,
- uint32_t device);
+ uint32_t device,
+ type_t type = MIXER);
virtual ~MixerThread();
// Thread virtuals
@@ -917,7 +919,7 @@
virtual status_t onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
private:
- sp<PlaybackThread::Track> mTrack;
+ const sp<PlaybackThread::Track> mTrack;
};
friend class Client;
@@ -1021,8 +1023,8 @@
int16_t *mRsmpInBuffer;
size_t mRsmpInIndex;
size_t mInputBytes;
- int mReqChannelCount;
- uint32_t mReqSampleRate;
+ const int mReqChannelCount;
+ const uint32_t mReqSampleRate;
ssize_t mBytesRead;
};
@@ -1036,7 +1038,7 @@
virtual status_t onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
private:
- sp<RecordThread::RecordTrack> mRecordTrack;
+ const sp<RecordThread::RecordTrack> mRecordTrack;
};
//--- Audio Effect Management
@@ -1105,9 +1107,9 @@
int16_t *outBuffer() { return mConfig.outputCfg.buffer.s16; }
void setChain(const wp<EffectChain>& chain) { mChain = chain; }
void setThread(const wp<ThreadBase>& thread) { mThread = thread; }
- wp<ThreadBase>& thread() { return mThread; }
+ const wp<ThreadBase>& thread() { return mThread; }
- status_t addHandle(sp<EffectHandle>& handle);
+ status_t addHandle(const sp<EffectHandle>& handle);
void disconnect(const wp<EffectHandle>& handle, bool unpiniflast);
size_t removeHandle (const wp<EffectHandle>& handle);
@@ -1325,7 +1327,8 @@
// get a list of effect modules to suspend when an effect of the type
// passed is enabled.
- Vector< sp<EffectModule> > getSuspendEligibleEffects();
+ void getSuspendEligibleEffects(Vector< sp<EffectModule> > &effects);
+
// get an effect module if it is currently enable
sp<EffectModule> getEffectIfEnabled(const effect_uuid_t *type);
// true if the effect whose descriptor is passed can be suspended
@@ -1377,8 +1380,11 @@
};
struct AudioSessionRef {
- int sessionid;
- pid_t pid;
+ // FIXME rename parameter names when fields get "m" prefix
+ AudioSessionRef(int sessionid_, pid_t pid_) :
+ sessionid(sessionid_), pid(pid_), cnt(1) {}
+ const int sessionid;
+ const pid_t pid;
int cnt;
};
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index a8102e5..0b9f8ba 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -48,9 +48,10 @@
mState.enabledTracks= 0;
mState.needsChanged = 0;
mState.frameCount = frameCount;
+ mState.hook = process__nop;
mState.outputTemp = NULL;
mState.resampleTemp = NULL;
- mState.hook = process__nop;
+ // mState.reserved
track_t* t = mState.tracks;
for (unsigned i=0 ; i < MAX_NUM_TRACKS ; i++) {
t->needs = 0;
@@ -70,12 +71,13 @@
t->enabled = 0;
t->format = 16;
t->channelMask = AUDIO_CHANNEL_OUT_STEREO;
- t->buffer.raw = 0;
t->bufferProvider = NULL;
+ t->buffer.raw = NULL;
+ // t->buffer.frameCount
t->hook = NULL;
+ t->in = NULL;
t->resampler = NULL;
t->sampleRate = mSampleRate;
- t->in = NULL;
t->mainBuffer = NULL;
t->auxBuffer = NULL;
t++;
@@ -123,7 +125,7 @@
track.enabled = 0;
invalidateState(1<<name);
}
- if (track.resampler) {
+ if (track.resampler != NULL) {
// delete the resampler
delete track.resampler;
track.resampler = NULL;
@@ -807,7 +809,7 @@
while (outFrames) {
t1.buffer.frameCount = outFrames;
t1.bufferProvider->getNextBuffer(&t1.buffer);
- if (!t1.buffer.raw) break;
+ if (t1.buffer.raw == NULL) break;
outFrames -= t1.buffer.frameCount;
t1.bufferProvider->releaseBuffer(&t1.buffer);
}
@@ -1127,9 +1129,7 @@
}
}
- if (buff != NULL) {
- delete [] buff;
- }
+ delete [] buff;
}
#endif
diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp
index 2df1385..1dddbb3 100644
--- a/services/audioflinger/AudioPolicyService.cpp
+++ b/services/audioflinger/AudioPolicyService.cpp
@@ -144,9 +144,9 @@
}
mInputs.clear();
- if (mpAudioPolicy && mpAudioPolicyDev)
+ if (mpAudioPolicy != NULL && mpAudioPolicyDev != NULL)
mpAudioPolicyDev->destroy_audio_policy(mpAudioPolicyDev, mpAudioPolicy);
- if (mpAudioPolicyDev)
+ if (mpAudioPolicyDev != NULL)
audio_policy_dev_close(mpAudioPolicyDev);
}
@@ -649,7 +649,7 @@
release_wake_lock(mName.string());
}
mAudioCommands.clear();
- if (mpToneGenerator != NULL) delete mpToneGenerator;
+ delete mpToneGenerator;
}
void AudioPolicyService::AudioCommandThread::onFirstRef()
@@ -682,8 +682,7 @@
ToneData *data = (ToneData *)command->mParam;
ALOGV("AudioCommandThread() processing start tone %d on stream %d",
data->mType, data->mStream);
- if (mpToneGenerator != NULL)
- delete mpToneGenerator;
+ delete mpToneGenerator;
mpToneGenerator = new ToneGenerator(data->mStream, 1.0);
mpToneGenerator->startTone(data->mType);
delete data;
@@ -790,7 +789,8 @@
return NO_ERROR;
}
-void AudioPolicyService::AudioCommandThread::startToneCommand(int type, audio_stream_type_t stream)
+void AudioPolicyService::AudioCommandThread::startToneCommand(ToneGenerator::tone_type type,
+ audio_stream_type_t stream)
{
AudioCommand *command = new AudioCommand();
command->mCommand = START_TONE;
@@ -1160,7 +1160,7 @@
if (param == NULL && value == NULL) {
// try to parse simple parameter form {int int}
param = root->first_child;
- if (param) {
+ if (param != NULL) {
// Note: that a pair of random strings is read as 0 0
int *ptr = (int *)fx_param->data;
int *ptr2 = (int *)((char *)param + sizeof(effect_param_t));
@@ -1419,7 +1419,7 @@
uint32_t *pSamplingRate,
audio_format_t *pFormat,
uint32_t *pChannels,
- uint32_t acoustics)
+ audio_in_acoustics_t acoustics)
{
sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
if (af == NULL) {
diff --git a/services/audioflinger/AudioPolicyService.h b/services/audioflinger/AudioPolicyService.h
index 3c0f5ed..62219e5 100644
--- a/services/audioflinger/AudioPolicyService.h
+++ b/services/audioflinger/AudioPolicyService.h
@@ -79,7 +79,7 @@
audio_format_t format = AUDIO_FORMAT_DEFAULT,
uint32_t channels = 0,
audio_in_acoustics_t acoustics =
- (audio_in_acoustics_t)0,
+ (audio_in_acoustics_t)0 /*AUDIO_IN_ACOUSTICS_NONE*/,
int audioSession = 0);
virtual status_t startInput(audio_io_handle_t input);
virtual status_t stopInput(audio_io_handle_t input);
@@ -171,7 +171,8 @@
virtual bool threadLoop();
void exit();
- void startToneCommand(int type = 0, audio_stream_type_t stream = AUDIO_STREAM_VOICE_CALL);
+ void startToneCommand(ToneGenerator::tone_type type,
+ audio_stream_type_t stream);
void stopToneCommand();
status_t volumeCommand(audio_stream_type_t stream, float volume, int output, int delayMs = 0);
status_t parametersCommand(int ioHandle, const char *keyValuePairs, int delayMs = 0);
@@ -198,7 +199,7 @@
class ToneData {
public:
- int mType; // tone type (START_TONE only)
+ ToneGenerator::tone_type mType; // tone type (START_TONE only)
audio_stream_type_t mStream; // stream type (START_TONE only)
};
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 4f81178..081f1f4 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -24,67 +24,35 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.Intent.FilterComparison;
import android.content.IntentFilter;
import android.content.ServiceConnection;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.net.Uri;
import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.SystemClock;
-import android.util.AttributeSet;
-import android.util.Log;
import android.util.Pair;
import android.util.Slog;
-import android.util.TypedValue;
-import android.util.Xml;
+import android.util.SparseArray;
import android.widget.RemoteViews;
import com.android.internal.appwidget.IAppWidgetHost;
import com.android.internal.appwidget.IAppWidgetService;
-import com.android.internal.os.AtomicFile;
-import com.android.internal.util.FastXmlSerializer;
import com.android.internal.widget.IRemoteViewsAdapterConnection;
-import com.android.internal.widget.IRemoteViewsFactory;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
import java.util.Locale;
-import java.util.Set;
+
+/**
+ * Redirects calls to this service to the instance of the service for the appropriate user.
+ */
class AppWidgetService extends IAppWidgetService.Stub
{
private static final String TAG = "AppWidgetService";
- private static final String SETTINGS_FILENAME = "appwidgets.xml";
- private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
-
/*
* When identifying a Host or Provider based on the calling process, use the uid field.
* When identifying a Host or Provider based on a package manager broadcast, use the
@@ -125,11 +93,9 @@
* globally and may lead us to leak AppWidgetService instances (if there were more than one).
*/
static class ServiceConnectionProxy implements ServiceConnection {
- private final Pair<Integer, Intent.FilterComparison> mKey;
private final IBinder mConnectionCb;
ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
- mKey = key;
mConnectionCb = connectionCb;
}
public void onServiceConnected(ComponentName name, IBinder service) {
@@ -155,13 +121,6 @@
}
}
- // Manages active connections to RemoteViewsServices
- private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection>
- mBoundRemoteViewsServices = new HashMap<Pair<Integer,FilterComparison>,ServiceConnection>();
- // Manages persistent references to RemoteViewsServices from different App Widgets
- private final HashMap<FilterComparison, HashSet<Integer>>
- mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
-
Context mContext;
Locale mLocale;
PackageManager mPackageManager;
@@ -171,35 +130,32 @@
final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
ArrayList<Host> mHosts = new ArrayList<Host>();
boolean mSafeMode;
- boolean mStateLoaded;
- // These are for debugging only -- widgets are going missing in some rare instances
- ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
- ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
+
+ private final SparseArray<AppWidgetServiceImpl> mAppWidgetServices;
AppWidgetService(Context context) {
mContext = context;
- mPackageManager = context.getPackageManager();
- mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
+ mAppWidgetServices = new SparseArray<AppWidgetServiceImpl>(5);
+ AppWidgetServiceImpl primary = new AppWidgetServiceImpl(context, 0);
+ mAppWidgetServices.append(0, primary);
}
public void systemReady(boolean safeMode) {
mSafeMode = safeMode;
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- }
+ mAppWidgetServices.get(0).systemReady(safeMode);
// Register for the boot completed broadcast, so we can send the
- // ENABLE broacasts. If we try to send them now, they time out,
+ // ENABLE broacasts. If we try to send them now, they time out,
// because the system isn't ready to handle them yet.
mContext.registerReceiver(mBroadcastReceiver,
new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
// Register for configuration changes so we can update the names
// of the widgets when the locale changes.
- mContext.registerReceiver(mBroadcastReceiver,
- new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);
+ mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(
+ Intent.ACTION_CONFIGURATION_CHANGED), null, null);
// Register for broadcasts about package install, etc., so we can
// update the provider list.
@@ -216,216 +172,24 @@
mContext.registerReceiver(mBroadcastReceiver, sdFilter);
}
- private void ensureStateLoadedLocked() {
- if (!mStateLoaded) {
- loadAppWidgetList();
- loadStateLocked();
- mStateLoaded = true;
- }
+ @Override
+ public int allocateAppWidgetId(String packageName, int hostId) throws RemoteException {
+ return getImplForUser().allocateAppWidgetId(packageName, hostId);
}
-
- private void dumpProvider(Provider p, int index, PrintWriter pw) {
- AppWidgetProviderInfo info = p.info;
- pw.print(" ["); pw.print(index); pw.print("] provider ");
- pw.print(info.provider.flattenToShortString());
- pw.println(':');
- pw.print(" min=("); pw.print(info.minWidth);
- pw.print("x"); pw.print(info.minHeight);
- pw.print(") minResize=("); pw.print(info.minResizeWidth);
- pw.print("x"); pw.print(info.minResizeHeight);
- pw.print(") updatePeriodMillis=");
- pw.print(info.updatePeriodMillis);
- pw.print(" resizeMode=");
- pw.print(info.resizeMode);
- pw.print(" autoAdvanceViewId=");
- pw.print(info.autoAdvanceViewId);
- pw.print(" initialLayout=#");
- pw.print(Integer.toHexString(info.initialLayout));
- pw.print(" zombie="); pw.println(p.zombie);
- }
-
- private void dumpHost(Host host, int index, PrintWriter pw) {
- pw.print(" ["); pw.print(index); pw.print("] hostId=");
- pw.print(host.hostId); pw.print(' ');
- pw.print(host.packageName); pw.print('/');
- pw.print(host.uid); pw.println(':');
- pw.print(" callbacks="); pw.println(host.callbacks);
- pw.print(" instances.size="); pw.print(host.instances.size());
- pw.print(" zombie="); pw.println(host.zombie);
- }
-
- private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
- pw.print(" ["); pw.print(index); pw.print("] id=");
- pw.println(id.appWidgetId);
- pw.print(" hostId=");
- pw.print(id.host.hostId); pw.print(' ');
- pw.print(id.host.packageName); pw.print('/');
- pw.println(id.host.uid);
- if (id.provider != null) {
- pw.print(" provider=");
- pw.println(id.provider.info.provider.flattenToShortString());
- }
- if (id.host != null) {
- pw.print(" host.callbacks="); pw.println(id.host.callbacks);
- }
- if (id.views != null) {
- pw.print(" views="); pw.println(id.views);
- }
+
+ @Override
+ public void deleteAppWidgetId(int appWidgetId) throws RemoteException {
+ getImplForUser().deleteAppWidgetId(appWidgetId);
}
@Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
- != PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump from from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid());
- return;
- }
-
- synchronized (mAppWidgetIds) {
- int N = mInstalledProviders.size();
- pw.println("Providers:");
- for (int i=0; i<N; i++) {
- dumpProvider(mInstalledProviders.get(i), i, pw);
- }
-
- N = mAppWidgetIds.size();
- pw.println(" ");
- pw.println("AppWidgetIds:");
- for (int i=0; i<N; i++) {
- dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
- }
-
- N = mHosts.size();
- pw.println(" ");
- pw.println("Hosts:");
- for (int i=0; i<N; i++) {
- dumpHost(mHosts.get(i), i, pw);
- }
-
- N = mDeletedProviders.size();
- pw.println(" ");
- pw.println("Deleted Providers:");
- for (int i=0; i<N; i++) {
- dumpProvider(mDeletedProviders.get(i), i, pw);
- }
-
- N = mDeletedHosts.size();
- pw.println(" ");
- pw.println("Deleted Hosts:");
- for (int i=0; i<N; i++) {
- dumpHost(mDeletedHosts.get(i), i, pw);
- }
- }
+ public void deleteHost(int hostId) throws RemoteException {
+ getImplForUser().deleteHost(hostId);
}
- public int allocateAppWidgetId(String packageName, int hostId) {
- int callingUid = enforceCallingUid(packageName);
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- int appWidgetId = mNextAppWidgetId++;
-
- Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
-
- AppWidgetId id = new AppWidgetId();
- id.appWidgetId = appWidgetId;
- id.host = host;
-
- host.instances.add(id);
- mAppWidgetIds.add(id);
-
- saveStateLocked();
-
- return appWidgetId;
- }
- }
-
- public void deleteAppWidgetId(int appWidgetId) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
- if (id != null) {
- deleteAppWidgetLocked(id);
- saveStateLocked();
- }
- }
- }
-
- public void deleteHost(int hostId) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- int callingUid = getCallingUid();
- Host host = lookupHostLocked(callingUid, hostId);
- if (host != null) {
- deleteHostLocked(host);
- saveStateLocked();
- }
- }
- }
-
- public void deleteAllHosts() {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- int callingUid = getCallingUid();
- final int N = mHosts.size();
- boolean changed = false;
- for (int i=N-1; i>=0; i--) {
- Host host = mHosts.get(i);
- if (host.uid == callingUid) {
- deleteHostLocked(host);
- changed = true;
- }
- }
- if (changed) {
- saveStateLocked();
- }
- }
- }
-
- void deleteHostLocked(Host host) {
- final int N = host.instances.size();
- for (int i=N-1; i>=0; i--) {
- AppWidgetId id = host.instances.get(i);
- deleteAppWidgetLocked(id);
- }
- host.instances.clear();
- mHosts.remove(host);
- mDeletedHosts.add(host);
- // it's gone or going away, abruptly drop the callback connection
- host.callbacks = null;
- }
-
- void deleteAppWidgetLocked(AppWidgetId id) {
- // We first unbind all services that are bound to this id
- unbindAppWidgetRemoteViewsServicesLocked(id);
-
- Host host = id.host;
- host.instances.remove(id);
- pruneHostLocked(host);
-
- mAppWidgetIds.remove(id);
-
- Provider p = id.provider;
- if (p != null) {
- p.instances.remove(id);
- if (!p.zombie) {
- // send the broacast saying that this appWidgetId has been deleted
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
- intent.setComponent(p.info.provider);
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
- mContext.sendBroadcast(intent);
- if (p.instances.size() == 0) {
- // cancel the future updates
- cancelBroadcasts(p);
-
- // send the broacast saying that the provider is not in use any more
- intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
- intent.setComponent(p.info.provider);
- mContext.sendBroadcast(intent);
- }
- }
- }
+ @Override
+ public void deleteAllHosts() throws RemoteException {
+ getImplForUser().deleteAllHosts();
}
void cancelBroadcasts(Provider p) {
@@ -441,617 +205,58 @@
}
}
- public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
- mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
- "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
-
- final long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
- if (id == null) {
- throw new IllegalArgumentException("bad appWidgetId");
- }
- if (id.provider != null) {
- throw new IllegalArgumentException("appWidgetId " + appWidgetId + " already bound to "
- + id.provider.info.provider);
- }
- Provider p = lookupProviderLocked(provider);
- if (p == null) {
- throw new IllegalArgumentException("not a appwidget provider: " + provider);
- }
- if (p.zombie) {
- throw new IllegalArgumentException("can't bind to a 3rd party provider in"
- + " safe mode: " + provider);
- }
-
- id.provider = p;
- p.instances.add(id);
- int instancesSize = p.instances.size();
- if (instancesSize == 1) {
- // tell the provider that it's ready
- sendEnableIntentLocked(p);
- }
-
- // send an update now -- We need this update now, and just for this appWidgetId.
- // It's less critical when the next one happens, so when we schdule the next one,
- // we add updatePeriodMillis to its start time. That time will have some slop,
- // but that's okay.
- sendUpdateIntentLocked(p, new int[] { appWidgetId });
-
- // schedule the future updates
- registerForBroadcastsLocked(p, getAppWidgetIds(p));
- saveStateLocked();
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
+ @Override
+ public void bindAppWidgetId(int appWidgetId, ComponentName provider) throws RemoteException {
+ getImplForUser().bindAppWidgetId(appWidgetId, provider);
}
- // Binds to a specific RemoteViewsService
- public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
- if (id == null) {
- throw new IllegalArgumentException("bad appWidgetId");
- }
- final ComponentName componentName = intent.getComponent();
- try {
- final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
- PackageManager.GET_PERMISSIONS);
- if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
- throw new SecurityException("Selected service does not require "
- + android.Manifest.permission.BIND_REMOTEVIEWS
- + ": " + componentName);
- }
- } catch (PackageManager.NameNotFoundException e) {
- throw new IllegalArgumentException("Unknown component " + componentName);
- }
-
- // If there is already a connection made for this service intent, then disconnect from
- // that first. (This does not allow multiple connections to the same service under
- // the same key)
- ServiceConnectionProxy conn = null;
- FilterComparison fc = new FilterComparison(intent);
- Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
- if (mBoundRemoteViewsServices.containsKey(key)) {
- conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
- conn.disconnect();
- mContext.unbindService(conn);
- mBoundRemoteViewsServices.remove(key);
- }
-
- // Bind to the RemoteViewsService (which will trigger a callback to the
- // RemoteViewsAdapter.onServiceConnected())
- final long token = Binder.clearCallingIdentity();
- try {
- conn = new ServiceConnectionProxy(key, connection);
- mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
- mBoundRemoteViewsServices.put(key, conn);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
-
- // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
- // when we can call back to the RemoteViewsService later to destroy associated
- // factories.
- incrementAppWidgetServiceRefCount(appWidgetId, fc);
- }
+ @Override
+ public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection)
+ throws RemoteException {
+ getImplForUser().bindRemoteViewsService(appWidgetId, intent, connection);
}
- // Unbinds from a specific RemoteViewsService
- public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- // Unbind from the RemoteViewsService (which will trigger a callback to the bound
- // RemoteViewsAdapter)
- Pair<Integer, FilterComparison> key = Pair.create(appWidgetId,
- new FilterComparison(intent));
- if (mBoundRemoteViewsServices.containsKey(key)) {
- // We don't need to use the appWidgetId until after we are sure there is something
- // to unbind. Note that this may mask certain issues with apps calling unbind()
- // more than necessary.
- AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
- if (id == null) {
- throw new IllegalArgumentException("bad appWidgetId");
- }
-
- ServiceConnectionProxy conn =
- (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
- conn.disconnect();
- mContext.unbindService(conn);
- mBoundRemoteViewsServices.remove(key);
- } else {
- Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
- }
- }
+ @Override
+ public int[] startListening(IAppWidgetHost host, String packageName, int hostId,
+ List<RemoteViews> updatedViews) throws RemoteException {
+ return getImplForUser().startListening(host, packageName, hostId, updatedViews);
}
- // Unbinds from a RemoteViewsService when we delete an app widget
- private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
- int appWidgetId = id.appWidgetId;
- // Unbind all connections to Services bound to this AppWidgetId
- Iterator<Pair<Integer, Intent.FilterComparison>> it =
- mBoundRemoteViewsServices.keySet().iterator();
- while (it.hasNext()) {
- final Pair<Integer, Intent.FilterComparison> key = it.next();
- if (key.first.intValue() == appWidgetId) {
- final ServiceConnectionProxy conn = (ServiceConnectionProxy)
- mBoundRemoteViewsServices.get(key);
- conn.disconnect();
- mContext.unbindService(conn);
- it.remove();
- }
- }
-
- // Check if we need to destroy any services (if no other app widgets are
- // referencing the same service)
- decrementAppWidgetServiceRefCount(appWidgetId);
+ // TODO: Call this from PackageManagerService when a user is removed
+ public void removeUser(int userId) {
}
- // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
- private void destroyRemoteViewsService(final Intent intent) {
- final ServiceConnection conn = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- final IRemoteViewsFactory cb =
- IRemoteViewsFactory.Stub.asInterface(service);
- try {
- cb.onDestroy(intent);
- } catch (RemoteException e) {
- e.printStackTrace();
- } catch (RuntimeException e) {
- e.printStackTrace();
- }
- mContext.unbindService(this);
- }
- @Override
- public void onServiceDisconnected(android.content.ComponentName name) {
- // Do nothing
- }
- };
-
- // Bind to the service and remove the static intent->factory mapping in the
- // RemoteViewsService.
- final long token = Binder.clearCallingIdentity();
- try {
- mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
- } finally {
- Binder.restoreCallingIdentity(token);
+ private AppWidgetServiceImpl getImplForUser() {
+ final int userId = Binder.getOrigCallingUser();
+ AppWidgetServiceImpl service = mAppWidgetServices.get(userId);
+ if (service == null) {
+ Slog.e(TAG, "Unable to find AppWidgetServiceImpl for the current user");
+ // TODO: Verify that it's a valid user
+ service = new AppWidgetServiceImpl(mContext, userId);
+ service.systemReady(mSafeMode);
+ // Assume that BOOT_COMPLETED was received, as this is a non-primary user.
+ service.sendInitialBroadcasts();
+ mAppWidgetServices.append(userId, service);
}
+
+ return service;
}
- // Adds to the ref-count for a given RemoteViewsService intent
- private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
- HashSet<Integer> appWidgetIds = null;
- if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
- appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
- } else {
- appWidgetIds = new HashSet<Integer>();
- mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
- }
- appWidgetIds.add(appWidgetId);
+ @Override
+ public int[] getAppWidgetIds(ComponentName provider) throws RemoteException {
+ return getImplForUser().getAppWidgetIds(provider);
}
- // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
- // the ref-count reaches zero.
- private void decrementAppWidgetServiceRefCount(int appWidgetId) {
- Iterator<FilterComparison> it =
- mRemoteViewsServicesAppWidgets.keySet().iterator();
- while (it.hasNext()) {
- final FilterComparison key = it.next();
- final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
- if (ids.remove(appWidgetId)) {
- // If we have removed the last app widget referencing this service, then we
- // should destroy it and remove it from this set
- if (ids.isEmpty()) {
- destroyRemoteViewsService(key.getIntent());
- it.remove();
- }
- }
- }
+ @Override
+ public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) throws RemoteException {
+ return getImplForUser().getAppWidgetInfo(appWidgetId);
}
- public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
- if (id != null && id.provider != null && !id.provider.zombie) {
- return id.provider.info;
- }
- return null;
- }
+ @Override
+ public RemoteViews getAppWidgetViews(int appWidgetId) throws RemoteException {
+ return getImplForUser().getAppWidgetViews(appWidgetId);
}
- public RemoteViews getAppWidgetViews(int appWidgetId) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
- if (id != null) {
- return id.views;
- }
- return null;
- }
- }
-
- public List<AppWidgetProviderInfo> getInstalledProviders() {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- final int N = mInstalledProviders.size();
- ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
- for (int i=0; i<N; i++) {
- Provider p = mInstalledProviders.get(i);
- if (!p.zombie) {
- result.add(p.info);
- }
- }
- return result;
- }
- }
-
- public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
- if (appWidgetIds == null) {
- return;
- }
- if (appWidgetIds.length == 0) {
- return;
- }
- final int N = appWidgetIds.length;
-
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- for (int i=0; i<N; i++) {
- AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
- updateAppWidgetInstanceLocked(id, views);
- }
- }
- }
-
- public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
- if (appWidgetIds == null) {
- return;
- }
- if (appWidgetIds.length == 0) {
- return;
- }
- final int N = appWidgetIds.length;
-
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- for (int i=0; i<N; i++) {
- AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
- updateAppWidgetInstanceLocked(id, views, true);
- }
- }
- }
-
- public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
- if (appWidgetIds == null) {
- return;
- }
- if (appWidgetIds.length == 0) {
- return;
- }
- final int N = appWidgetIds.length;
-
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- for (int i=0; i<N; i++) {
- AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
- notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
- }
- }
- }
-
- public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- Provider p = lookupProviderLocked(provider);
- if (p == null) {
- Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
- return;
- }
- ArrayList<AppWidgetId> instances = p.instances;
- final int callingUid = getCallingUid();
- final int N = instances.size();
- for (int i=0; i<N; i++) {
- AppWidgetId id = instances.get(i);
- if (canAccessAppWidgetId(id, callingUid)) {
- updateAppWidgetInstanceLocked(id, views);
- }
- }
- }
- }
-
- void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
- updateAppWidgetInstanceLocked(id, views, false);
- }
-
- void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
- // allow for stale appWidgetIds and other badness
- // lookup also checks that the calling process can access the appWidgetId
- // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
- if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
-
- // We do not want to save this RemoteViews
- if (!isPartialUpdate) id.views = views;
-
- // is anyone listening?
- if (id.host.callbacks != null) {
- try {
- // the lock is held, but this is a oneway call
- id.host.callbacks.updateAppWidget(id.appWidgetId, views);
- } catch (RemoteException e) {
- // It failed; remove the callback. No need to prune because
- // we know that this host is still referenced by this instance.
- id.host.callbacks = null;
- }
- }
- }
- }
-
- void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
- // allow for stale appWidgetIds and other badness
- // lookup also checks that the calling process can access the appWidgetId
- // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
- if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
- // is anyone listening?
- if (id.host.callbacks != null) {
- try {
- // the lock is held, but this is a oneway call
- id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
- } catch (RemoteException e) {
- // It failed; remove the callback. No need to prune because
- // we know that this host is still referenced by this instance.
- id.host.callbacks = null;
- }
- }
-
- // If the host is unavailable, then we call the associated
- // RemoteViewsFactory.onDataSetChanged() directly
- if (id.host.callbacks == null) {
- Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
- for (FilterComparison key : keys) {
- if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
- Intent intent = key.getIntent();
-
- final ServiceConnection conn = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- IRemoteViewsFactory cb =
- IRemoteViewsFactory.Stub.asInterface(service);
- try {
- cb.onDataSetChangedAsync();
- } catch (RemoteException e) {
- e.printStackTrace();
- } catch (RuntimeException e) {
- e.printStackTrace();
- }
- mContext.unbindService(this);
- }
- @Override
- public void onServiceDisconnected(android.content.ComponentName name) {
- // Do nothing
- }
- };
-
- // Bind to the service and call onDataSetChanged()
- final long token = Binder.clearCallingIdentity();
- try {
- mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- }
- }
- }
- }
-
- public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
- List<RemoteViews> updatedViews) {
- int callingUid = enforceCallingUid(packageName);
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
- host.callbacks = callbacks;
-
- updatedViews.clear();
-
- ArrayList<AppWidgetId> instances = host.instances;
- int N = instances.size();
- int[] updatedIds = new int[N];
- for (int i=0; i<N; i++) {
- AppWidgetId id = instances.get(i);
- updatedIds[i] = id.appWidgetId;
- updatedViews.add(id.views);
- }
- return updatedIds;
- }
- }
-
- public void stopListening(int hostId) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- Host host = lookupHostLocked(getCallingUid(), hostId);
- if (host != null) {
- host.callbacks = null;
- pruneHostLocked(host);
- }
- }
- }
-
- boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
- if (id.host.uid == callingUid) {
- // Apps hosting the AppWidget have access to it.
- return true;
- }
- if (id.provider != null && id.provider.uid == callingUid) {
- // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
- return true;
- }
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET)
- == PackageManager.PERMISSION_GRANTED) {
- // Apps that can bind have access to all appWidgetIds.
- return true;
- }
- // Nobody else can access it.
- return false;
- }
-
- AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
- int callingUid = getCallingUid();
- final int N = mAppWidgetIds.size();
- for (int i=0; i<N; i++) {
- AppWidgetId id = mAppWidgetIds.get(i);
- if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
- return id;
- }
- }
- return null;
- }
-
- Provider lookupProviderLocked(ComponentName provider) {
- final int N = mInstalledProviders.size();
- for (int i=0; i<N; i++) {
- Provider p = mInstalledProviders.get(i);
- if (p.info.provider.equals(provider)) {
- return p;
- }
- }
- return null;
- }
-
- Host lookupHostLocked(int uid, int hostId) {
- final int N = mHosts.size();
- for (int i=0; i<N; i++) {
- Host h = mHosts.get(i);
- if (h.uid == uid && h.hostId == hostId) {
- return h;
- }
- }
- return null;
- }
-
- Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
- final int N = mHosts.size();
- for (int i=0; i<N; i++) {
- Host h = mHosts.get(i);
- if (h.hostId == hostId && h.packageName.equals(packageName)) {
- return h;
- }
- }
- Host host = new Host();
- host.packageName = packageName;
- host.uid = uid;
- host.hostId = hostId;
- mHosts.add(host);
- return host;
- }
-
- void pruneHostLocked(Host host) {
- if (host.instances.size() == 0 && host.callbacks == null) {
- mHosts.remove(host);
- }
- }
-
- void loadAppWidgetList() {
- PackageManager pm = mPackageManager;
-
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
- List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
- PackageManager.GET_META_DATA);
-
- final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
- for (int i=0; i<N; i++) {
- ResolveInfo ri = broadcastReceivers.get(i);
- addProviderLocked(ri);
- }
- }
-
- boolean addProviderLocked(ResolveInfo ri) {
- if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
- return false;
- }
- if (!ri.activityInfo.isEnabled()) {
- return false;
- }
- Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
- ri.activityInfo.name), ri);
- if (p != null) {
- mInstalledProviders.add(p);
- return true;
- } else {
- return false;
- }
- }
-
- void removeProviderLocked(int index, Provider p) {
- int N = p.instances.size();
- for (int i=0; i<N; i++) {
- AppWidgetId id = p.instances.get(i);
- // Call back with empty RemoteViews
- updateAppWidgetInstanceLocked(id, null);
- // Stop telling the host about updates for this from now on
- cancelBroadcasts(p);
- // clear out references to this appWidgetId
- id.host.instances.remove(id);
- mAppWidgetIds.remove(id);
- id.provider = null;
- pruneHostLocked(id.host);
- id.host = null;
- }
- p.instances.clear();
- mInstalledProviders.remove(index);
- mDeletedProviders.add(p);
- // no need to send the DISABLE broadcast, since the receiver is gone anyway
- cancelBroadcasts(p);
- }
-
- void sendEnableIntentLocked(Provider p) {
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
- intent.setComponent(p.info.provider);
- mContext.sendBroadcast(intent);
- }
-
- void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
- if (appWidgetIds != null && appWidgetIds.length > 0) {
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
- intent.setComponent(p.info.provider);
- mContext.sendBroadcast(intent);
- }
- }
-
- void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
- if (p.info.updatePeriodMillis > 0) {
- // if this is the first instance, set the alarm. otherwise,
- // rely on the fact that we've already set it and that
- // PendingIntent.getBroadcast will update the extras.
- boolean alreadyRegistered = p.broadcast != null;
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
- intent.setComponent(p.info.provider);
- long token = Binder.clearCallingIdentity();
- try {
- p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- if (!alreadyRegistered) {
- long period = p.info.updatePeriodMillis;
- if (period < MIN_UPDATE_PERIOD) {
- period = MIN_UPDATE_PERIOD;
- }
- mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + period, period, p.broadcast);
- }
- }
- }
-
static int[] getAppWidgetIds(Provider p) {
int instancesSize = p.instances.size();
int appWidgetIds[] = new int[instancesSize];
@@ -1060,570 +265,70 @@
}
return appWidgetIds;
}
-
- public int[] getAppWidgetIds(ComponentName provider) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- Provider p = lookupProviderLocked(provider);
- if (p != null && getCallingUid() == p.uid) {
- return getAppWidgetIds(p);
- } else {
- return new int[0];
- }
- }
+
+ @Override
+ public List<AppWidgetProviderInfo> getInstalledProviders() throws RemoteException {
+ return getImplForUser().getInstalledProviders();
}
- private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
- Provider p = null;
-
- ActivityInfo activityInfo = ri.activityInfo;
- XmlResourceParser parser = null;
- try {
- parser = activityInfo.loadXmlMetaData(mPackageManager,
- AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
- if (parser == null) {
- Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for "
- + "AppWidget provider '" + component + '\'');
- return null;
- }
-
- AttributeSet attrs = Xml.asAttributeSet(parser);
-
- int type;
- while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
- && type != XmlPullParser.START_TAG) {
- // drain whitespace, comments, etc.
- }
-
- String nodeName = parser.getName();
- if (!"appwidget-provider".equals(nodeName)) {
- Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
- + " AppWidget provider '" + component + '\'');
- return null;
- }
-
- p = new Provider();
- AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
- info.provider = component;
- p.uid = activityInfo.applicationInfo.uid;
-
- Resources res = mPackageManager.getResourcesForApplication(
- activityInfo.applicationInfo);
-
- TypedArray sa = res.obtainAttributes(attrs,
- com.android.internal.R.styleable.AppWidgetProviderInfo);
-
- // These dimensions has to be resolved in the application's context.
- // We simply send back the raw complex data, which will be
- // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
- TypedValue value = sa.peekValue(
- com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
- info.minWidth = value != null ? value.data : 0;
- value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
- info.minHeight = value != null ? value.data : 0;
- value = sa.peekValue(
- com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
- info.minResizeWidth = value != null ? value.data : info.minWidth;
- value = sa.peekValue(
- com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
- info.minResizeHeight = value != null ? value.data : info.minHeight;
-
- info.updatePeriodMillis = sa.getInt(
- com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
- info.initialLayout = sa.getResourceId(
- com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
- String className = sa.getString(
- com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
- if (className != null) {
- info.configure = new ComponentName(component.getPackageName(), className);
- }
- info.label = activityInfo.loadLabel(mPackageManager).toString();
- info.icon = ri.getIconResource();
- info.previewImage = sa.getResourceId(
- com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
- info.autoAdvanceViewId = sa.getResourceId(
- com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
- info.resizeMode = sa.getInt(
- com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
- AppWidgetProviderInfo.RESIZE_NONE);
-
- sa.recycle();
- } catch (Exception e) {
- // Ok to catch Exception here, because anything going wrong because
- // of what a client process passes to us should not be fatal for the
- // system process.
- Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
- return null;
- } finally {
- if (parser != null) parser.close();
- }
- return p;
+ @Override
+ public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId)
+ throws RemoteException {
+ getImplForUser().notifyAppWidgetViewDataChanged(appWidgetIds, viewId);
}
- int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
- PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
- if (pkgInfo == null || pkgInfo.applicationInfo == null) {
- throw new PackageManager.NameNotFoundException();
- }
- return pkgInfo.applicationInfo.uid;
+ @Override
+ public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views)
+ throws RemoteException {
+ getImplForUser().partiallyUpdateAppWidgetIds(appWidgetIds, views);
}
- int enforceCallingUid(String packageName) throws IllegalArgumentException {
- int callingUid = getCallingUid();
- int packageUid;
- try {
- packageUid = getUidForPackage(packageName);
- } catch (PackageManager.NameNotFoundException ex) {
- throw new IllegalArgumentException("packageName and uid don't match packageName="
- + packageName);
- }
- if (callingUid != packageUid) {
- throw new IllegalArgumentException("packageName and uid don't match packageName="
- + packageName);
- }
- return callingUid;
+ @Override
+ public void stopListening(int hostId) throws RemoteException {
+ getImplForUser().stopListening(hostId);
}
- void sendInitialBroadcasts() {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- final int N = mInstalledProviders.size();
- for (int i=0; i<N; i++) {
- Provider p = mInstalledProviders.get(i);
- if (p.instances.size() > 0) {
- sendEnableIntentLocked(p);
- int[] appWidgetIds = getAppWidgetIds(p);
- sendUpdateIntentLocked(p, appWidgetIds);
- registerForBroadcastsLocked(p, appWidgetIds);
- }
- }
- }
+ @Override
+ public void unbindRemoteViewsService(int appWidgetId, Intent intent) throws RemoteException {
+ getImplForUser().unbindRemoteViewsService(appWidgetId, intent);
}
- // only call from initialization -- it assumes that the data structures are all empty
- void loadStateLocked() {
- AtomicFile file = savedStateFile();
- try {
- FileInputStream stream = file.openRead();
- readStateFromFileLocked(stream);
-
- if (stream != null) {
- try {
- stream.close();
- } catch (IOException e) {
- Slog.w(TAG, "Failed to close state FileInputStream " + e);
- }
- }
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "Failed to read state: " + e);
- }
+ @Override
+ public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) throws RemoteException {
+ getImplForUser().updateAppWidgetIds(appWidgetIds, views);
}
- void saveStateLocked() {
- AtomicFile file = savedStateFile();
- FileOutputStream stream;
- try {
- stream = file.startWrite();
- if (writeStateToFileLocked(stream)) {
- file.finishWrite(stream);
- } else {
- file.failWrite(stream);
- Slog.w(TAG, "Failed to save state, restoring backup.");
- }
- } catch (IOException e) {
- Slog.w(TAG, "Failed open state file for write: " + e);
- }
+ @Override
+ public void updateAppWidgetProvider(ComponentName provider, RemoteViews views)
+ throws RemoteException {
+ getImplForUser().updateAppWidgetProvider(provider, views);
}
- boolean writeStateToFileLocked(FileOutputStream stream) {
- int N;
-
- try {
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(stream, "utf-8");
- out.startDocument(null, true);
- out.startTag(null, "gs");
-
- int providerIndex = 0;
- N = mInstalledProviders.size();
- for (int i=0; i<N; i++) {
- Provider p = mInstalledProviders.get(i);
- if (p.instances.size() > 0) {
- out.startTag(null, "p");
- out.attribute(null, "pkg", p.info.provider.getPackageName());
- out.attribute(null, "cl", p.info.provider.getClassName());
- out.endTag(null, "p");
- p.tag = providerIndex;
- providerIndex++;
- }
- }
-
- N = mHosts.size();
- for (int i=0; i<N; i++) {
- Host host = mHosts.get(i);
- out.startTag(null, "h");
- out.attribute(null, "pkg", host.packageName);
- out.attribute(null, "id", Integer.toHexString(host.hostId));
- out.endTag(null, "h");
- host.tag = i;
- }
-
- N = mAppWidgetIds.size();
- for (int i=0; i<N; i++) {
- AppWidgetId id = mAppWidgetIds.get(i);
- out.startTag(null, "g");
- out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
- out.attribute(null, "h", Integer.toHexString(id.host.tag));
- if (id.provider != null) {
- out.attribute(null, "p", Integer.toHexString(id.provider.tag));
- }
- out.endTag(null, "g");
- }
-
- out.endTag(null, "gs");
-
- out.endDocument();
- return true;
- } catch (IOException e) {
- Slog.w(TAG, "Failed to write state: " + e);
- return false;
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ // Dump the state of all the app widget providers
+ for (int i = 0; i < mAppWidgetServices.size(); i++) {
+ AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
+ service.dump(fd, pw, args);
}
}
- void readStateFromFileLocked(FileInputStream stream) {
- boolean success = false;
-
- try {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(stream, null);
-
- int type;
- int providerIndex = 0;
- HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
- do {
- type = parser.next();
- if (type == XmlPullParser.START_TAG) {
- String tag = parser.getName();
- if ("p".equals(tag)) {
- // TODO: do we need to check that this package has the same signature
- // as before?
- String pkg = parser.getAttributeValue(null, "pkg");
- String cl = parser.getAttributeValue(null, "cl");
-
- final PackageManager packageManager = mContext.getPackageManager();
- try {
- packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
- } catch (PackageManager.NameNotFoundException e) {
- String[] pkgs = packageManager.currentToCanonicalPackageNames(
- new String[] { pkg });
- pkg = pkgs[0];
- }
-
- Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
- if (p == null && mSafeMode) {
- // if we're in safe mode, make a temporary one
- p = new Provider();
- p.info = new AppWidgetProviderInfo();
- p.info.provider = new ComponentName(pkg, cl);
- p.zombie = true;
- mInstalledProviders.add(p);
- }
- if (p != null) {
- // if it wasn't uninstalled or something
- loadedProviders.put(providerIndex, p);
- }
- providerIndex++;
- }
- else if ("h".equals(tag)) {
- Host host = new Host();
-
- // TODO: do we need to check that this package has the same signature
- // as before?
- host.packageName = parser.getAttributeValue(null, "pkg");
- try {
- host.uid = getUidForPackage(host.packageName);
- } catch (PackageManager.NameNotFoundException ex) {
- host.zombie = true;
- }
- if (!host.zombie || mSafeMode) {
- // In safe mode, we don't discard the hosts we don't recognize
- // so that they're not pruned from our list. Otherwise, we do.
- host.hostId = Integer.parseInt(
- parser.getAttributeValue(null, "id"), 16);
- mHosts.add(host);
- }
- }
- else if ("g".equals(tag)) {
- AppWidgetId id = new AppWidgetId();
- id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
- if (id.appWidgetId >= mNextAppWidgetId) {
- mNextAppWidgetId = id.appWidgetId + 1;
- }
-
- String providerString = parser.getAttributeValue(null, "p");
- if (providerString != null) {
- // there's no provider if it hasn't been bound yet.
- // maybe we don't have to save this, but it brings the system
- // to the state it was in.
- int pIndex = Integer.parseInt(providerString, 16);
- id.provider = loadedProviders.get(pIndex);
- if (false) {
- Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
- + pIndex + " which is " + id.provider);
- }
- if (id.provider == null) {
- // This provider is gone. We just let the host figure out
- // that this happened when it fails to load it.
- continue;
- }
- }
-
- int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
- id.host = mHosts.get(hIndex);
- if (id.host == null) {
- // This host is gone.
- continue;
- }
-
- if (id.provider != null) {
- id.provider.instances.add(id);
- }
- id.host.instances.add(id);
- mAppWidgetIds.add(id);
- }
- }
- } while (type != XmlPullParser.END_DOCUMENT);
- success = true;
- } catch (NullPointerException e) {
- Slog.w(TAG, "failed parsing " + e);
- } catch (NumberFormatException e) {
- Slog.w(TAG, "failed parsing " + e);
- } catch (XmlPullParserException e) {
- Slog.w(TAG, "failed parsing " + e);
- } catch (IOException e) {
- Slog.w(TAG, "failed parsing " + e);
- } catch (IndexOutOfBoundsException e) {
- Slog.w(TAG, "failed parsing " + e);
- }
-
- if (success) {
- // delete any hosts that didn't manage to get connected (should happen)
- // if it matters, they'll be reconnected.
- for (int i=mHosts.size()-1; i>=0; i--) {
- pruneHostLocked(mHosts.get(i));
- }
- } else {
- // failed reading, clean up
- Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
-
- mAppWidgetIds.clear();
- mHosts.clear();
- final int N = mInstalledProviders.size();
- for (int i=0; i<N; i++) {
- mInstalledProviders.get(i).instances.clear();
- }
- }
- }
-
- AtomicFile savedStateFile() {
- return new AtomicFile(new File("/data/system/" + SETTINGS_FILENAME));
- }
-
BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- //Slog.d(TAG, "received " + action);
+ // Slog.d(TAG, "received " + action);
if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
- sendInitialBroadcasts();
+ getImplForUser().sendInitialBroadcasts();
} else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
- Locale revised = Locale.getDefault();
- if (revised == null || mLocale == null ||
- !(revised.equals(mLocale))) {
- mLocale = revised;
-
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- int N = mInstalledProviders.size();
- for (int i=N-1; i>=0; i--) {
- Provider p = mInstalledProviders.get(i);
- String pkgName = p.info.provider.getPackageName();
- updateProvidersForPackageLocked(pkgName);
- }
- saveStateLocked();
- }
+ for (int i = 0; i < mAppWidgetServices.size(); i++) {
+ AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
+ service.onConfigurationChanged();
}
} else {
- boolean added = false;
- boolean changed = false;
- String pkgList[] = null;
- if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
- pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- added = true;
- } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
- pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- added = false;
- } else {
- Uri uri = intent.getData();
- if (uri == null) {
- return;
- }
- String pkgName = uri.getSchemeSpecificPart();
- if (pkgName == null) {
- return;
- }
- pkgList = new String[] { pkgName };
- added = Intent.ACTION_PACKAGE_ADDED.equals(action);
- changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
- }
- if (pkgList == null || pkgList.length == 0) {
- return;
- }
- if (added || changed) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- Bundle extras = intent.getExtras();
- if (changed || (extras != null &&
- extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
- for (String pkgName : pkgList) {
- // The package was just upgraded
- updateProvidersForPackageLocked(pkgName);
- }
- } else {
- // The package was just added
- for (String pkgName : pkgList) {
- addProvidersForPackageLocked(pkgName);
- }
- }
- saveStateLocked();
- }
- } else {
- Bundle extras = intent.getExtras();
- if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
- // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
- } else {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- for (String pkgName : pkgList) {
- removeProvidersForPackageLocked(pkgName);
- saveStateLocked();
- }
- }
- }
- }
+ // TODO: Verify that this only needs to be delivered for the related user and not
+ // all the users
+ getImplForUser().onBroadcastReceived(intent);
}
}
};
-
- void addProvidersForPackageLocked(String pkgName) {
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
- intent.setPackage(pkgName);
- List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
- PackageManager.GET_META_DATA);
-
- final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
- for (int i=0; i<N; i++) {
- ResolveInfo ri = broadcastReceivers.get(i);
- ActivityInfo ai = ri.activityInfo;
- if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
- continue;
- }
- if (pkgName.equals(ai.packageName)) {
- addProviderLocked(ri);
- }
- }
- }
-
- void updateProvidersForPackageLocked(String pkgName) {
- HashSet<String> keep = new HashSet<String>();
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
- intent.setPackage(pkgName);
- List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
- PackageManager.GET_META_DATA);
-
- // add the missing ones and collect which ones to keep
- int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
- for (int i=0; i<N; i++) {
- ResolveInfo ri = broadcastReceivers.get(i);
- ActivityInfo ai = ri.activityInfo;
- if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
- continue;
- }
- if (pkgName.equals(ai.packageName)) {
- ComponentName component = new ComponentName(ai.packageName, ai.name);
- Provider p = lookupProviderLocked(component);
- if (p == null) {
- if (addProviderLocked(ri)) {
- keep.add(ai.name);
- }
- } else {
- Provider parsed = parseProviderInfoXml(component, ri);
- if (parsed != null) {
- keep.add(ai.name);
- // Use the new AppWidgetProviderInfo.
- p.info = parsed.info;
- // If it's enabled
- final int M = p.instances.size();
- if (M > 0) {
- int[] appWidgetIds = getAppWidgetIds(p);
- // Reschedule for the new updatePeriodMillis (don't worry about handling
- // it specially if updatePeriodMillis didn't change because we just sent
- // an update, and the next one will be updatePeriodMillis from now).
- cancelBroadcasts(p);
- registerForBroadcastsLocked(p, appWidgetIds);
- // If it's currently showing, call back with the new AppWidgetProviderInfo.
- for (int j=0; j<M; j++) {
- AppWidgetId id = p.instances.get(j);
- id.views = null;
- if (id.host != null && id.host.callbacks != null) {
- try {
- id.host.callbacks.providerChanged(id.appWidgetId, p.info);
- } catch (RemoteException ex) {
- // It failed; remove the callback. No need to prune because
- // we know that this host is still referenced by this
- // instance.
- id.host.callbacks = null;
- }
- }
- }
- // Now that we've told the host, push out an update.
- sendUpdateIntentLocked(p, appWidgetIds);
- }
- }
- }
- }
- }
-
- // prune the ones we don't want to keep
- N = mInstalledProviders.size();
- for (int i=N-1; i>=0; i--) {
- Provider p = mInstalledProviders.get(i);
- if (pkgName.equals(p.info.provider.getPackageName())
- && !keep.contains(p.info.provider.getClassName())) {
- removeProviderLocked(i, p);
- }
- }
- }
-
- void removeProvidersForPackageLocked(String pkgName) {
- int N = mInstalledProviders.size();
- for (int i=N-1; i>=0; i--) {
- Provider p = mInstalledProviders.get(i);
- if (pkgName.equals(p.info.provider.getPackageName())) {
- removeProviderLocked(i, p);
- }
- }
-
- // Delete the hosts for this package too
- //
- // By now, we have removed any AppWidgets that were in any hosts here,
- // so we don't need to worry about sending DISABLE broadcasts to them.
- N = mHosts.size();
- for (int i=N-1; i>=0; i--) {
- Host host = mHosts.get(i);
- if (pkgName.equals(host.packageName)) {
- deleteHostLocked(host);
- }
- }
- }
}
-
diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java
new file mode 100644
index 0000000..250386f
--- /dev/null
+++ b/services/java/com/android/server/AppWidgetServiceImpl.java
@@ -0,0 +1,1606 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.Intent.FilterComparison;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserId;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.TypedValue;
+import android.util.Xml;
+import android.widget.RemoteViews;
+
+import com.android.internal.appwidget.IAppWidgetHost;
+import com.android.internal.os.AtomicFile;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.widget.IRemoteViewsAdapterConnection;
+import com.android.internal.widget.IRemoteViewsFactory;
+import com.android.server.am.ActivityManagerService;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+class AppWidgetServiceImpl {
+
+ private static final String TAG = "AppWidgetServiceImpl";
+ private static final String SETTINGS_FILENAME = "appwidgets.xml";
+ private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
+
+ /*
+ * When identifying a Host or Provider based on the calling process, use the uid field. When
+ * identifying a Host or Provider based on a package manager broadcast, use the package given.
+ */
+
+ static class Provider {
+ int uid;
+ AppWidgetProviderInfo info;
+ ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
+ PendingIntent broadcast;
+ boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
+
+ int tag; // for use while saving state (the index)
+ }
+
+ static class Host {
+ int uid;
+ int hostId;
+ String packageName;
+ ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
+ IAppWidgetHost callbacks;
+ boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
+
+ int tag; // for use while saving state (the index)
+ }
+
+ static class AppWidgetId {
+ int appWidgetId;
+ Provider provider;
+ RemoteViews views;
+ Host host;
+ }
+
+ /**
+ * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection. This
+ * needs to be a static inner class since a reference to the ServiceConnection is held globally
+ * and may lead us to leak AppWidgetService instances (if there were more than one).
+ */
+ static class ServiceConnectionProxy implements ServiceConnection {
+ private final IBinder mConnectionCb;
+
+ ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
+ mConnectionCb = connectionCb;
+ }
+
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
+ .asInterface(mConnectionCb);
+ try {
+ cb.onServiceConnected(service);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ disconnect();
+ }
+
+ public void disconnect() {
+ final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
+ .asInterface(mConnectionCb);
+ try {
+ cb.onServiceDisconnected();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ // Manages active connections to RemoteViewsServices
+ private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection> mBoundRemoteViewsServices = new HashMap<Pair<Integer, FilterComparison>, ServiceConnection>();
+ // Manages persistent references to RemoteViewsServices from different App Widgets
+ private final HashMap<FilterComparison, HashSet<Integer>> mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
+
+ Context mContext;
+ Locale mLocale;
+ PackageManager mPackageManager;
+ AlarmManager mAlarmManager;
+ ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
+ int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
+ final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
+ ArrayList<Host> mHosts = new ArrayList<Host>();
+ boolean mSafeMode;
+ int mUserId;
+ boolean mStateLoaded;
+
+ // These are for debugging only -- widgets are going missing in some rare instances
+ ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
+ ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
+
+ AppWidgetServiceImpl(Context context, int userId) {
+ mContext = context;
+ mPackageManager = context.getPackageManager();
+ mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ mUserId = userId;
+ }
+
+ public void systemReady(boolean safeMode) {
+ mSafeMode = safeMode;
+
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ }
+ }
+
+ void onConfigurationChanged() {
+ Locale revised = Locale.getDefault();
+ if (revised == null || mLocale == null || !(revised.equals(mLocale))) {
+ mLocale = revised;
+
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ int N = mInstalledProviders.size();
+ for (int i = N - 1; i >= 0; i--) {
+ Provider p = mInstalledProviders.get(i);
+ String pkgName = p.info.provider.getPackageName();
+ updateProvidersForPackageLocked(pkgName);
+ }
+ saveStateLocked();
+ }
+ }
+ }
+
+ void onBroadcastReceived(Intent intent) {
+ final String action = intent.getAction();
+ boolean added = false;
+ boolean changed = false;
+ String pkgList[] = null;
+ if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
+ pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ added = true;
+ } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
+ pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ added = false;
+ } else {
+ Uri uri = intent.getData();
+ if (uri == null) {
+ return;
+ }
+ String pkgName = uri.getSchemeSpecificPart();
+ if (pkgName == null) {
+ return;
+ }
+ pkgList = new String[] { pkgName };
+ added = Intent.ACTION_PACKAGE_ADDED.equals(action);
+ changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
+ }
+ if (pkgList == null || pkgList.length == 0) {
+ return;
+ }
+ if (added || changed) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ Bundle extras = intent.getExtras();
+ if (changed
+ || (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
+ for (String pkgName : pkgList) {
+ // The package was just upgraded
+ updateProvidersForPackageLocked(pkgName);
+ }
+ } else {
+ // The package was just added
+ for (String pkgName : pkgList) {
+ addProvidersForPackageLocked(pkgName);
+ }
+ }
+ saveStateLocked();
+ }
+ } else {
+ Bundle extras = intent.getExtras();
+ if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
+ // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
+ } else {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ for (String pkgName : pkgList) {
+ removeProvidersForPackageLocked(pkgName);
+ saveStateLocked();
+ }
+ }
+ }
+ }
+ }
+
+ private void dumpProvider(Provider p, int index, PrintWriter pw) {
+ AppWidgetProviderInfo info = p.info;
+ pw.print(" ["); pw.print(index); pw.print("] provider ");
+ pw.print(info.provider.flattenToShortString());
+ pw.println(':');
+ pw.print(" min=("); pw.print(info.minWidth);
+ pw.print("x"); pw.print(info.minHeight);
+ pw.print(") minResize=("); pw.print(info.minResizeWidth);
+ pw.print("x"); pw.print(info.minResizeHeight);
+ pw.print(") updatePeriodMillis=");
+ pw.print(info.updatePeriodMillis);
+ pw.print(" resizeMode=");
+ pw.print(info.resizeMode);
+ pw.print(" autoAdvanceViewId=");
+ pw.print(info.autoAdvanceViewId);
+ pw.print(" initialLayout=#");
+ pw.print(Integer.toHexString(info.initialLayout));
+ pw.print(" zombie="); pw.println(p.zombie);
+ }
+
+ private void dumpHost(Host host, int index, PrintWriter pw) {
+ pw.print(" ["); pw.print(index); pw.print("] hostId=");
+ pw.print(host.hostId); pw.print(' ');
+ pw.print(host.packageName); pw.print('/');
+ pw.print(host.uid); pw.println(':');
+ pw.print(" callbacks="); pw.println(host.callbacks);
+ pw.print(" instances.size="); pw.print(host.instances.size());
+ pw.print(" zombie="); pw.println(host.zombie);
+ }
+
+ private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
+ pw.print(" ["); pw.print(index); pw.print("] id=");
+ pw.println(id.appWidgetId);
+ pw.print(" hostId=");
+ pw.print(id.host.hostId); pw.print(' ');
+ pw.print(id.host.packageName); pw.print('/');
+ pw.println(id.host.uid);
+ if (id.provider != null) {
+ pw.print(" provider=");
+ pw.println(id.provider.info.provider.flattenToShortString());
+ }
+ if (id.host != null) {
+ pw.print(" host.callbacks="); pw.println(id.host.callbacks);
+ }
+ if (id.views != null) {
+ pw.print(" views="); pw.println(id.views);
+ }
+ }
+
+ void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ synchronized (mAppWidgetIds) {
+ int N = mInstalledProviders.size();
+ pw.println("Providers:");
+ for (int i=0; i<N; i++) {
+ dumpProvider(mInstalledProviders.get(i), i, pw);
+ }
+
+ N = mAppWidgetIds.size();
+ pw.println(" ");
+ pw.println("AppWidgetIds:");
+ for (int i=0; i<N; i++) {
+ dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
+ }
+
+ N = mHosts.size();
+ pw.println(" ");
+ pw.println("Hosts:");
+ for (int i=0; i<N; i++) {
+ dumpHost(mHosts.get(i), i, pw);
+ }
+
+ N = mDeletedProviders.size();
+ pw.println(" ");
+ pw.println("Deleted Providers:");
+ for (int i=0; i<N; i++) {
+ dumpProvider(mDeletedProviders.get(i), i, pw);
+ }
+
+ N = mDeletedHosts.size();
+ pw.println(" ");
+ pw.println("Deleted Hosts:");
+ for (int i=0; i<N; i++) {
+ dumpHost(mDeletedHosts.get(i), i, pw);
+ }
+ }
+ }
+
+ private void ensureStateLoadedLocked() {
+ if (!mStateLoaded) {
+ loadAppWidgetList();
+ loadStateLocked();
+ mStateLoaded = true;
+ }
+ }
+
+ public int allocateAppWidgetId(String packageName, int hostId) {
+ int callingUid = enforceCallingUid(packageName);
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ int appWidgetId = mNextAppWidgetId++;
+
+ Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
+
+ AppWidgetId id = new AppWidgetId();
+ id.appWidgetId = appWidgetId;
+ id.host = host;
+
+ host.instances.add(id);
+ mAppWidgetIds.add(id);
+
+ saveStateLocked();
+
+ return appWidgetId;
+ }
+ }
+
+ public void deleteAppWidgetId(int appWidgetId) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+ if (id != null) {
+ deleteAppWidgetLocked(id);
+ saveStateLocked();
+ }
+ }
+ }
+
+ public void deleteHost(int hostId) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ int callingUid = Binder.getCallingUid();
+ Host host = lookupHostLocked(callingUid, hostId);
+ if (host != null) {
+ deleteHostLocked(host);
+ saveStateLocked();
+ }
+ }
+ }
+
+ public void deleteAllHosts() {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ int callingUid = Binder.getCallingUid();
+ final int N = mHosts.size();
+ boolean changed = false;
+ for (int i = N - 1; i >= 0; i--) {
+ Host host = mHosts.get(i);
+ if (host.uid == callingUid) {
+ deleteHostLocked(host);
+ changed = true;
+ }
+ }
+ if (changed) {
+ saveStateLocked();
+ }
+ }
+ }
+
+ void deleteHostLocked(Host host) {
+ final int N = host.instances.size();
+ for (int i = N - 1; i >= 0; i--) {
+ AppWidgetId id = host.instances.get(i);
+ deleteAppWidgetLocked(id);
+ }
+ host.instances.clear();
+ mHosts.remove(host);
+ mDeletedHosts.add(host);
+ // it's gone or going away, abruptly drop the callback connection
+ host.callbacks = null;
+ }
+
+ void deleteAppWidgetLocked(AppWidgetId id) {
+ // We first unbind all services that are bound to this id
+ unbindAppWidgetRemoteViewsServicesLocked(id);
+
+ Host host = id.host;
+ host.instances.remove(id);
+ pruneHostLocked(host);
+
+ mAppWidgetIds.remove(id);
+
+ Provider p = id.provider;
+ if (p != null) {
+ p.instances.remove(id);
+ if (!p.zombie) {
+ // send the broacast saying that this appWidgetId has been deleted
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
+ intent.setComponent(p.info.provider);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
+ mContext.sendBroadcast(intent);
+ if (p.instances.size() == 0) {
+ // cancel the future updates
+ cancelBroadcasts(p);
+
+ // send the broacast saying that the provider is not in use any more
+ intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
+ intent.setComponent(p.info.provider);
+ mContext.sendBroadcast(intent);
+ }
+ }
+ }
+ }
+
+ void cancelBroadcasts(Provider p) {
+ if (p.broadcast != null) {
+ mAlarmManager.cancel(p.broadcast);
+ long token = Binder.clearCallingIdentity();
+ try {
+ p.broadcast.cancel();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ p.broadcast = null;
+ }
+ }
+
+ public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
+ mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
+ "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+ if (id == null) {
+ throw new IllegalArgumentException("bad appWidgetId");
+ }
+ if (id.provider != null) {
+ throw new IllegalArgumentException("appWidgetId " + appWidgetId
+ + " already bound to " + id.provider.info.provider);
+ }
+ Provider p = lookupProviderLocked(provider);
+ if (p == null) {
+ throw new IllegalArgumentException("not a appwidget provider: " + provider);
+ }
+ if (p.zombie) {
+ throw new IllegalArgumentException("can't bind to a 3rd party provider in"
+ + " safe mode: " + provider);
+ }
+
+ Binder.restoreCallingIdentity(ident);
+
+ id.provider = p;
+ p.instances.add(id);
+ int instancesSize = p.instances.size();
+ if (instancesSize == 1) {
+ // tell the provider that it's ready
+ sendEnableIntentLocked(p);
+ }
+
+ // send an update now -- We need this update now, and just for this appWidgetId.
+ // It's less critical when the next one happens, so when we schedule the next one,
+ // we add updatePeriodMillis to its start time. That time will have some slop,
+ // but that's okay.
+ sendUpdateIntentLocked(p, new int[] { appWidgetId });
+
+ // schedule the future updates
+ registerForBroadcastsLocked(p, getAppWidgetIds(p));
+ saveStateLocked();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ // Binds to a specific RemoteViewsService
+ public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+ if (id == null) {
+ throw new IllegalArgumentException("bad appWidgetId");
+ }
+ final ComponentName componentName = intent.getComponent();
+ try {
+ final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
+ PackageManager.GET_PERMISSIONS);
+ if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
+ throw new SecurityException("Selected service does not require "
+ + android.Manifest.permission.BIND_REMOTEVIEWS + ": " + componentName);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new IllegalArgumentException("Unknown component " + componentName);
+ }
+
+ // If there is already a connection made for this service intent, then disconnect from
+ // that first. (This does not allow multiple connections to the same service under
+ // the same key)
+ ServiceConnectionProxy conn = null;
+ FilterComparison fc = new FilterComparison(intent);
+ Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
+ if (mBoundRemoteViewsServices.containsKey(key)) {
+ conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
+ conn.disconnect();
+ mContext.unbindService(conn);
+ mBoundRemoteViewsServices.remove(key);
+ }
+
+ // Bind to the RemoteViewsService (which will trigger a callback to the
+ // RemoteViewsAdapter.onServiceConnected())
+ final long token = Binder.clearCallingIdentity();
+ try {
+ conn = new ServiceConnectionProxy(key, connection);
+ mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
+ mBoundRemoteViewsServices.put(key, conn);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
+ // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
+ // when we can call back to the RemoteViewsService later to destroy associated
+ // factories.
+ incrementAppWidgetServiceRefCount(appWidgetId, fc);
+ }
+ }
+
+ // Unbinds from a specific RemoteViewsService
+ public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ // Unbind from the RemoteViewsService (which will trigger a callback to the bound
+ // RemoteViewsAdapter)
+ Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, new FilterComparison(
+ intent));
+ if (mBoundRemoteViewsServices.containsKey(key)) {
+ // We don't need to use the appWidgetId until after we are sure there is something
+ // to unbind. Note that this may mask certain issues with apps calling unbind()
+ // more than necessary.
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+ if (id == null) {
+ throw new IllegalArgumentException("bad appWidgetId");
+ }
+
+ ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
+ .get(key);
+ conn.disconnect();
+ mContext.unbindService(conn);
+ mBoundRemoteViewsServices.remove(key);
+ } else {
+ Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
+ }
+ }
+ }
+
+ // Unbinds from a RemoteViewsService when we delete an app widget
+ private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
+ int appWidgetId = id.appWidgetId;
+ // Unbind all connections to Services bound to this AppWidgetId
+ Iterator<Pair<Integer, Intent.FilterComparison>> it = mBoundRemoteViewsServices.keySet()
+ .iterator();
+ while (it.hasNext()) {
+ final Pair<Integer, Intent.FilterComparison> key = it.next();
+ if (key.first.intValue() == appWidgetId) {
+ final ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
+ .get(key);
+ conn.disconnect();
+ mContext.unbindService(conn);
+ it.remove();
+ }
+ }
+
+ // Check if we need to destroy any services (if no other app widgets are
+ // referencing the same service)
+ decrementAppWidgetServiceRefCount(appWidgetId);
+ }
+
+ // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
+ private void destroyRemoteViewsService(final Intent intent) {
+ final ServiceConnection conn = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ final IRemoteViewsFactory cb = IRemoteViewsFactory.Stub.asInterface(service);
+ try {
+ cb.onDestroy(intent);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ } catch (RuntimeException e) {
+ e.printStackTrace();
+ }
+ mContext.unbindService(this);
+ }
+
+ @Override
+ public void onServiceDisconnected(android.content.ComponentName name) {
+ // Do nothing
+ }
+ };
+
+ // Bind to the service and remove the static intent->factory mapping in the
+ // RemoteViewsService.
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ // Adds to the ref-count for a given RemoteViewsService intent
+ private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
+ HashSet<Integer> appWidgetIds = null;
+ if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
+ appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
+ } else {
+ appWidgetIds = new HashSet<Integer>();
+ mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
+ }
+ appWidgetIds.add(appWidgetId);
+ }
+
+ // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
+ // the ref-count reaches zero.
+ private void decrementAppWidgetServiceRefCount(int appWidgetId) {
+ Iterator<FilterComparison> it = mRemoteViewsServicesAppWidgets.keySet().iterator();
+ while (it.hasNext()) {
+ final FilterComparison key = it.next();
+ final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
+ if (ids.remove(appWidgetId)) {
+ // If we have removed the last app widget referencing this service, then we
+ // should destroy it and remove it from this set
+ if (ids.isEmpty()) {
+ destroyRemoteViewsService(key.getIntent());
+ it.remove();
+ }
+ }
+ }
+ }
+
+ public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+ if (id != null && id.provider != null && !id.provider.zombie) {
+ return id.provider.info;
+ }
+ return null;
+ }
+ }
+
+ public RemoteViews getAppWidgetViews(int appWidgetId) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+ if (id != null) {
+ return id.views;
+ }
+ return null;
+ }
+ }
+
+ public List<AppWidgetProviderInfo> getInstalledProviders() {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ final int N = mInstalledProviders.size();
+ ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
+ for (int i = 0; i < N; i++) {
+ Provider p = mInstalledProviders.get(i);
+ if (!p.zombie) {
+ result.add(p.info);
+ }
+ }
+ return result;
+ }
+ }
+
+ public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
+ if (appWidgetIds == null) {
+ return;
+ }
+ if (appWidgetIds.length == 0) {
+ return;
+ }
+ final int N = appWidgetIds.length;
+
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ for (int i = 0; i < N; i++) {
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
+ updateAppWidgetInstanceLocked(id, views);
+ }
+ }
+ }
+
+ public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
+ if (appWidgetIds == null) {
+ return;
+ }
+ if (appWidgetIds.length == 0) {
+ return;
+ }
+ final int N = appWidgetIds.length;
+
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ for (int i = 0; i < N; i++) {
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
+ updateAppWidgetInstanceLocked(id, views, true);
+ }
+ }
+ }
+
+ public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
+ if (appWidgetIds == null) {
+ return;
+ }
+ if (appWidgetIds.length == 0) {
+ return;
+ }
+ final int N = appWidgetIds.length;
+
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ for (int i = 0; i < N; i++) {
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
+ notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
+ }
+ }
+ }
+
+ public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ Provider p = lookupProviderLocked(provider);
+ if (p == null) {
+ Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
+ return;
+ }
+ ArrayList<AppWidgetId> instances = p.instances;
+ final int callingUid = Binder.getCallingUid();
+ final int N = instances.size();
+ for (int i = 0; i < N; i++) {
+ AppWidgetId id = instances.get(i);
+ if (canAccessAppWidgetId(id, callingUid)) {
+ updateAppWidgetInstanceLocked(id, views);
+ }
+ }
+ }
+ }
+
+ void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
+ updateAppWidgetInstanceLocked(id, views, false);
+ }
+
+ void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
+ // allow for stale appWidgetIds and other badness
+ // lookup also checks that the calling process can access the appWidgetId
+ // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
+ if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
+
+ // We do not want to save this RemoteViews
+ if (!isPartialUpdate)
+ id.views = views;
+
+ // is anyone listening?
+ if (id.host.callbacks != null) {
+ try {
+ // the lock is held, but this is a oneway call
+ id.host.callbacks.updateAppWidget(id.appWidgetId, views);
+ } catch (RemoteException e) {
+ // It failed; remove the callback. No need to prune because
+ // we know that this host is still referenced by this instance.
+ id.host.callbacks = null;
+ }
+ }
+ }
+ }
+
+ void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
+ // allow for stale appWidgetIds and other badness
+ // lookup also checks that the calling process can access the appWidgetId
+ // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
+ if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
+ // is anyone listening?
+ if (id.host.callbacks != null) {
+ try {
+ // the lock is held, but this is a oneway call
+ id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
+ } catch (RemoteException e) {
+ // It failed; remove the callback. No need to prune because
+ // we know that this host is still referenced by this instance.
+ id.host.callbacks = null;
+ }
+ }
+
+ // If the host is unavailable, then we call the associated
+ // RemoteViewsFactory.onDataSetChanged() directly
+ if (id.host.callbacks == null) {
+ Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
+ for (FilterComparison key : keys) {
+ if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
+ Intent intent = key.getIntent();
+
+ final ServiceConnection conn = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ IRemoteViewsFactory cb = IRemoteViewsFactory.Stub
+ .asInterface(service);
+ try {
+ cb.onDataSetChangedAsync();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ } catch (RuntimeException e) {
+ e.printStackTrace();
+ }
+ mContext.unbindService(this);
+ }
+
+ @Override
+ public void onServiceDisconnected(android.content.ComponentName name) {
+ // Do nothing
+ }
+ };
+
+ // Bind to the service and call onDataSetChanged()
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
+ List<RemoteViews> updatedViews) {
+ int callingUid = enforceCallingUid(packageName);
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
+ host.callbacks = callbacks;
+
+ updatedViews.clear();
+
+ ArrayList<AppWidgetId> instances = host.instances;
+ int N = instances.size();
+ int[] updatedIds = new int[N];
+ for (int i = 0; i < N; i++) {
+ AppWidgetId id = instances.get(i);
+ updatedIds[i] = id.appWidgetId;
+ updatedViews.add(id.views);
+ }
+ return updatedIds;
+ }
+ }
+
+ public void stopListening(int hostId) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ Host host = lookupHostLocked(Binder.getCallingUid(), hostId);
+ if (host != null) {
+ host.callbacks = null;
+ pruneHostLocked(host);
+ }
+ }
+ }
+
+ boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
+ if (id.host.uid == callingUid) {
+ // Apps hosting the AppWidget have access to it.
+ return true;
+ }
+ if (id.provider != null && id.provider.uid == callingUid) {
+ // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
+ return true;
+ }
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET) == PackageManager.PERMISSION_GRANTED) {
+ // Apps that can bind have access to all appWidgetIds.
+ return true;
+ }
+ // Nobody else can access it.
+ return false;
+ }
+
+ AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
+ int callingUid = Binder.getCallingUid();
+ final int N = mAppWidgetIds.size();
+ for (int i = 0; i < N; i++) {
+ AppWidgetId id = mAppWidgetIds.get(i);
+ if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
+ return id;
+ }
+ }
+ return null;
+ }
+
+ Provider lookupProviderLocked(ComponentName provider) {
+ final int N = mInstalledProviders.size();
+ for (int i = 0; i < N; i++) {
+ Provider p = mInstalledProviders.get(i);
+ if (p.info.provider.equals(provider)) {
+ return p;
+ }
+ }
+ return null;
+ }
+
+ Host lookupHostLocked(int uid, int hostId) {
+ final int N = mHosts.size();
+ for (int i = 0; i < N; i++) {
+ Host h = mHosts.get(i);
+ if (h.uid == uid && h.hostId == hostId) {
+ return h;
+ }
+ }
+ return null;
+ }
+
+ Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
+ final int N = mHosts.size();
+ for (int i = 0; i < N; i++) {
+ Host h = mHosts.get(i);
+ if (h.hostId == hostId && h.packageName.equals(packageName)) {
+ return h;
+ }
+ }
+ Host host = new Host();
+ host.packageName = packageName;
+ host.uid = uid;
+ host.hostId = hostId;
+ mHosts.add(host);
+ return host;
+ }
+
+ void pruneHostLocked(Host host) {
+ if (host.instances.size() == 0 && host.callbacks == null) {
+ mHosts.remove(host);
+ }
+ }
+
+ void loadAppWidgetList() {
+ PackageManager pm = mPackageManager;
+
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+ List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
+ PackageManager.GET_META_DATA);
+
+ final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
+ for (int i = 0; i < N; i++) {
+ ResolveInfo ri = broadcastReceivers.get(i);
+ addProviderLocked(ri);
+ }
+ }
+
+ boolean addProviderLocked(ResolveInfo ri) {
+ if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
+ return false;
+ }
+ if (!ri.activityInfo.isEnabled()) {
+ return false;
+ }
+ Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
+ ri.activityInfo.name), ri);
+ if (p != null) {
+ mInstalledProviders.add(p);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ void removeProviderLocked(int index, Provider p) {
+ int N = p.instances.size();
+ for (int i = 0; i < N; i++) {
+ AppWidgetId id = p.instances.get(i);
+ // Call back with empty RemoteViews
+ updateAppWidgetInstanceLocked(id, null);
+ // Stop telling the host about updates for this from now on
+ cancelBroadcasts(p);
+ // clear out references to this appWidgetId
+ id.host.instances.remove(id);
+ mAppWidgetIds.remove(id);
+ id.provider = null;
+ pruneHostLocked(id.host);
+ id.host = null;
+ }
+ p.instances.clear();
+ mInstalledProviders.remove(index);
+ mDeletedProviders.add(p);
+ // no need to send the DISABLE broadcast, since the receiver is gone anyway
+ cancelBroadcasts(p);
+ }
+
+ void sendEnableIntentLocked(Provider p) {
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
+ intent.setComponent(p.info.provider);
+ mContext.sendBroadcast(intent);
+ }
+
+ void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
+ if (appWidgetIds != null && appWidgetIds.length > 0) {
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
+ intent.setComponent(p.info.provider);
+ mContext.sendBroadcast(intent);
+ }
+ }
+
+ void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
+ if (p.info.updatePeriodMillis > 0) {
+ // if this is the first instance, set the alarm. otherwise,
+ // rely on the fact that we've already set it and that
+ // PendingIntent.getBroadcast will update the extras.
+ boolean alreadyRegistered = p.broadcast != null;
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
+ intent.setComponent(p.info.provider);
+ long token = Binder.clearCallingIdentity();
+ try {
+ p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ if (!alreadyRegistered) {
+ long period = p.info.updatePeriodMillis;
+ if (period < MIN_UPDATE_PERIOD) {
+ period = MIN_UPDATE_PERIOD;
+ }
+ mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock
+ .elapsedRealtime()
+ + period, period, p.broadcast);
+ }
+ }
+ }
+
+ static int[] getAppWidgetIds(Provider p) {
+ int instancesSize = p.instances.size();
+ int appWidgetIds[] = new int[instancesSize];
+ for (int i = 0; i < instancesSize; i++) {
+ appWidgetIds[i] = p.instances.get(i).appWidgetId;
+ }
+ return appWidgetIds;
+ }
+
+ public int[] getAppWidgetIds(ComponentName provider) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ Provider p = lookupProviderLocked(provider);
+ if (p != null && Binder.getCallingUid() == p.uid) {
+ return getAppWidgetIds(p);
+ } else {
+ return new int[0];
+ }
+ }
+ }
+
+ private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
+ Provider p = null;
+
+ ActivityInfo activityInfo = ri.activityInfo;
+ XmlResourceParser parser = null;
+ try {
+ parser = activityInfo.loadXmlMetaData(mPackageManager,
+ AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
+ if (parser == null) {
+ Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER
+ + " meta-data for " + "AppWidget provider '" + component + '\'');
+ return null;
+ }
+
+ AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG) {
+ // drain whitespace, comments, etc.
+ }
+
+ String nodeName = parser.getName();
+ if (!"appwidget-provider".equals(nodeName)) {
+ Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
+ + " AppWidget provider '" + component + '\'');
+ return null;
+ }
+
+ p = new Provider();
+ AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
+ info.provider = component;
+ p.uid = activityInfo.applicationInfo.uid;
+
+ Resources res = mPackageManager
+ .getResourcesForApplication(activityInfo.applicationInfo);
+
+ TypedArray sa = res.obtainAttributes(attrs,
+ com.android.internal.R.styleable.AppWidgetProviderInfo);
+
+ // These dimensions has to be resolved in the application's context.
+ // We simply send back the raw complex data, which will be
+ // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
+ TypedValue value = sa
+ .peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
+ info.minWidth = value != null ? value.data : 0;
+ value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
+ info.minHeight = value != null ? value.data : 0;
+ value = sa.peekValue(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
+ info.minResizeWidth = value != null ? value.data : info.minWidth;
+ value = sa.peekValue(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
+ info.minResizeHeight = value != null ? value.data : info.minHeight;
+ info.updatePeriodMillis = sa.getInt(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
+ info.initialLayout = sa.getResourceId(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
+ String className = sa
+ .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
+ if (className != null) {
+ info.configure = new ComponentName(component.getPackageName(), className);
+ }
+ info.label = activityInfo.loadLabel(mPackageManager).toString();
+ info.icon = ri.getIconResource();
+ info.previewImage = sa.getResourceId(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
+ info.autoAdvanceViewId = sa.getResourceId(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
+ info.resizeMode = sa.getInt(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
+ AppWidgetProviderInfo.RESIZE_NONE);
+
+ sa.recycle();
+ } catch (Exception e) {
+ // Ok to catch Exception here, because anything going wrong because
+ // of what a client process passes to us should not be fatal for the
+ // system process.
+ Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
+ return null;
+ } finally {
+ if (parser != null)
+ parser.close();
+ }
+ return p;
+ }
+
+ int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
+ PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
+ if (pkgInfo == null || pkgInfo.applicationInfo == null) {
+ throw new PackageManager.NameNotFoundException();
+ }
+ return pkgInfo.applicationInfo.uid;
+ }
+
+ int enforceCallingUid(String packageName) throws IllegalArgumentException {
+ int callingUid = Binder.getCallingUid();
+ int packageUid;
+ try {
+ packageUid = getUidForPackage(packageName);
+ } catch (PackageManager.NameNotFoundException ex) {
+ throw new IllegalArgumentException("packageName and uid don't match packageName="
+ + packageName);
+ }
+ if (!UserId.isSameApp(callingUid, packageUid)) {
+ throw new IllegalArgumentException("packageName and uid don't match packageName="
+ + packageName);
+ }
+ return callingUid;
+ }
+
+ void sendInitialBroadcasts() {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ final int N = mInstalledProviders.size();
+ for (int i = 0; i < N; i++) {
+ Provider p = mInstalledProviders.get(i);
+ if (p.instances.size() > 0) {
+ sendEnableIntentLocked(p);
+ int[] appWidgetIds = getAppWidgetIds(p);
+ sendUpdateIntentLocked(p, appWidgetIds);
+ registerForBroadcastsLocked(p, appWidgetIds);
+ }
+ }
+ }
+ }
+
+ // only call from initialization -- it assumes that the data structures are all empty
+ void loadStateLocked() {
+ AtomicFile file = savedStateFile();
+ try {
+ FileInputStream stream = file.openRead();
+ readStateFromFileLocked(stream);
+
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to close state FileInputStream " + e);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "Failed to read state: " + e);
+ }
+ }
+
+ void saveStateLocked() {
+ AtomicFile file = savedStateFile();
+ FileOutputStream stream;
+ try {
+ stream = file.startWrite();
+ if (writeStateToFileLocked(stream)) {
+ file.finishWrite(stream);
+ } else {
+ file.failWrite(stream);
+ Slog.w(TAG, "Failed to save state, restoring backup.");
+ }
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed open state file for write: " + e);
+ }
+ }
+
+ boolean writeStateToFileLocked(FileOutputStream stream) {
+ int N;
+
+ try {
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(stream, "utf-8");
+ out.startDocument(null, true);
+ out.startTag(null, "gs");
+
+ int providerIndex = 0;
+ N = mInstalledProviders.size();
+ for (int i = 0; i < N; i++) {
+ Provider p = mInstalledProviders.get(i);
+ if (p.instances.size() > 0) {
+ out.startTag(null, "p");
+ out.attribute(null, "pkg", p.info.provider.getPackageName());
+ out.attribute(null, "cl", p.info.provider.getClassName());
+ out.endTag(null, "p");
+ p.tag = providerIndex;
+ providerIndex++;
+ }
+ }
+
+ N = mHosts.size();
+ for (int i = 0; i < N; i++) {
+ Host host = mHosts.get(i);
+ out.startTag(null, "h");
+ out.attribute(null, "pkg", host.packageName);
+ out.attribute(null, "id", Integer.toHexString(host.hostId));
+ out.endTag(null, "h");
+ host.tag = i;
+ }
+
+ N = mAppWidgetIds.size();
+ for (int i = 0; i < N; i++) {
+ AppWidgetId id = mAppWidgetIds.get(i);
+ out.startTag(null, "g");
+ out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
+ out.attribute(null, "h", Integer.toHexString(id.host.tag));
+ if (id.provider != null) {
+ out.attribute(null, "p", Integer.toHexString(id.provider.tag));
+ }
+ out.endTag(null, "g");
+ }
+
+ out.endTag(null, "gs");
+
+ out.endDocument();
+ return true;
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to write state: " + e);
+ return false;
+ }
+ }
+
+ void readStateFromFileLocked(FileInputStream stream) {
+ boolean success = false;
+
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(stream, null);
+
+ int type;
+ int providerIndex = 0;
+ HashMap<Integer, Provider> loadedProviders = new HashMap<Integer, Provider>();
+ do {
+ type = parser.next();
+ if (type == XmlPullParser.START_TAG) {
+ String tag = parser.getName();
+ if ("p".equals(tag)) {
+ // TODO: do we need to check that this package has the same signature
+ // as before?
+ String pkg = parser.getAttributeValue(null, "pkg");
+ String cl = parser.getAttributeValue(null, "cl");
+
+ final PackageManager packageManager = mContext.getPackageManager();
+ try {
+ packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ String[] pkgs = packageManager
+ .currentToCanonicalPackageNames(new String[] { pkg });
+ pkg = pkgs[0];
+ }
+
+ Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
+ if (p == null && mSafeMode) {
+ // if we're in safe mode, make a temporary one
+ p = new Provider();
+ p.info = new AppWidgetProviderInfo();
+ p.info.provider = new ComponentName(pkg, cl);
+ p.zombie = true;
+ mInstalledProviders.add(p);
+ }
+ if (p != null) {
+ // if it wasn't uninstalled or something
+ loadedProviders.put(providerIndex, p);
+ }
+ providerIndex++;
+ } else if ("h".equals(tag)) {
+ Host host = new Host();
+
+ // TODO: do we need to check that this package has the same signature
+ // as before?
+ host.packageName = parser.getAttributeValue(null, "pkg");
+ try {
+ host.uid = getUidForPackage(host.packageName);
+ } catch (PackageManager.NameNotFoundException ex) {
+ host.zombie = true;
+ }
+ if (!host.zombie || mSafeMode) {
+ // In safe mode, we don't discard the hosts we don't recognize
+ // so that they're not pruned from our list. Otherwise, we do.
+ host.hostId = Integer
+ .parseInt(parser.getAttributeValue(null, "id"), 16);
+ mHosts.add(host);
+ }
+ } else if ("g".equals(tag)) {
+ AppWidgetId id = new AppWidgetId();
+ id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
+ if (id.appWidgetId >= mNextAppWidgetId) {
+ mNextAppWidgetId = id.appWidgetId + 1;
+ }
+
+ String providerString = parser.getAttributeValue(null, "p");
+ if (providerString != null) {
+ // there's no provider if it hasn't been bound yet.
+ // maybe we don't have to save this, but it brings the system
+ // to the state it was in.
+ int pIndex = Integer.parseInt(providerString, 16);
+ id.provider = loadedProviders.get(pIndex);
+ if (false) {
+ Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
+ + pIndex + " which is " + id.provider);
+ }
+ if (id.provider == null) {
+ // This provider is gone. We just let the host figure out
+ // that this happened when it fails to load it.
+ continue;
+ }
+ }
+
+ int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
+ id.host = mHosts.get(hIndex);
+ if (id.host == null) {
+ // This host is gone.
+ continue;
+ }
+
+ if (id.provider != null) {
+ id.provider.instances.add(id);
+ }
+ id.host.instances.add(id);
+ mAppWidgetIds.add(id);
+ }
+ }
+ } while (type != XmlPullParser.END_DOCUMENT);
+ success = true;
+ } catch (NullPointerException e) {
+ Slog.w(TAG, "failed parsing " + e);
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "failed parsing " + e);
+ } catch (XmlPullParserException e) {
+ Slog.w(TAG, "failed parsing " + e);
+ } catch (IOException e) {
+ Slog.w(TAG, "failed parsing " + e);
+ } catch (IndexOutOfBoundsException e) {
+ Slog.w(TAG, "failed parsing " + e);
+ }
+
+ if (success) {
+ // delete any hosts that didn't manage to get connected (should happen)
+ // if it matters, they'll be reconnected.
+ for (int i = mHosts.size() - 1; i >= 0; i--) {
+ pruneHostLocked(mHosts.get(i));
+ }
+ } else {
+ // failed reading, clean up
+ Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
+
+ mAppWidgetIds.clear();
+ mHosts.clear();
+ final int N = mInstalledProviders.size();
+ for (int i = 0; i < N; i++) {
+ mInstalledProviders.get(i).instances.clear();
+ }
+ }
+ }
+
+ AtomicFile savedStateFile() {
+ int userId = Binder.getOrigCallingUser();
+ File dir = new File("/data/system/users/" + userId);
+ File settingsFile = new File(dir, SETTINGS_FILENAME);
+ if (!dir.exists()) {
+ dir.mkdirs();
+ if (userId == 0) {
+ // Migrate old data
+ File oldFile = new File("/data/system/" + SETTINGS_FILENAME);
+ // Method doesn't throw an exception on failure. Ignore any errors
+ // in moving the file (like non-existence)
+ oldFile.renameTo(settingsFile);
+ }
+ }
+ return new AtomicFile(settingsFile);
+ }
+
+ void addProvidersForPackageLocked(String pkgName) {
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+ intent.setPackage(pkgName);
+ List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
+ PackageManager.GET_META_DATA);
+
+ final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
+ for (int i = 0; i < N; i++) {
+ ResolveInfo ri = broadcastReceivers.get(i);
+ ActivityInfo ai = ri.activityInfo;
+ if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
+ continue;
+ }
+ if (pkgName.equals(ai.packageName)) {
+ addProviderLocked(ri);
+ }
+ }
+ }
+
+ void updateProvidersForPackageLocked(String pkgName) {
+ HashSet<String> keep = new HashSet<String>();
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+ intent.setPackage(pkgName);
+ List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
+ PackageManager.GET_META_DATA);
+
+ // add the missing ones and collect which ones to keep
+ int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
+ for (int i = 0; i < N; i++) {
+ ResolveInfo ri = broadcastReceivers.get(i);
+ ActivityInfo ai = ri.activityInfo;
+ if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
+ continue;
+ }
+ if (pkgName.equals(ai.packageName)) {
+ ComponentName component = new ComponentName(ai.packageName, ai.name);
+ Provider p = lookupProviderLocked(component);
+ if (p == null) {
+ if (addProviderLocked(ri)) {
+ keep.add(ai.name);
+ }
+ } else {
+ Provider parsed = parseProviderInfoXml(component, ri);
+ if (parsed != null) {
+ keep.add(ai.name);
+ // Use the new AppWidgetProviderInfo.
+ p.info = parsed.info;
+ // If it's enabled
+ final int M = p.instances.size();
+ if (M > 0) {
+ int[] appWidgetIds = getAppWidgetIds(p);
+ // Reschedule for the new updatePeriodMillis (don't worry about handling
+ // it specially if updatePeriodMillis didn't change because we just sent
+ // an update, and the next one will be updatePeriodMillis from now).
+ cancelBroadcasts(p);
+ registerForBroadcastsLocked(p, appWidgetIds);
+ // If it's currently showing, call back with the new
+ // AppWidgetProviderInfo.
+ for (int j = 0; j < M; j++) {
+ AppWidgetId id = p.instances.get(j);
+ id.views = null;
+ if (id.host != null && id.host.callbacks != null) {
+ try {
+ id.host.callbacks.providerChanged(id.appWidgetId, p.info);
+ } catch (RemoteException ex) {
+ // It failed; remove the callback. No need to prune because
+ // we know that this host is still referenced by this
+ // instance.
+ id.host.callbacks = null;
+ }
+ }
+ }
+ // Now that we've told the host, push out an update.
+ sendUpdateIntentLocked(p, appWidgetIds);
+ }
+ }
+ }
+ }
+ }
+
+ // prune the ones we don't want to keep
+ N = mInstalledProviders.size();
+ for (int i = N - 1; i >= 0; i--) {
+ Provider p = mInstalledProviders.get(i);
+ if (pkgName.equals(p.info.provider.getPackageName())
+ && !keep.contains(p.info.provider.getClassName())) {
+ removeProviderLocked(i, p);
+ }
+ }
+ }
+
+ void removeProvidersForPackageLocked(String pkgName) {
+ int N = mInstalledProviders.size();
+ for (int i = N - 1; i >= 0; i--) {
+ Provider p = mInstalledProviders.get(i);
+ if (pkgName.equals(p.info.provider.getPackageName())) {
+ removeProviderLocked(i, p);
+ }
+ }
+
+ // Delete the hosts for this package too
+ //
+ // By now, we have removed any AppWidgets that were in any hosts here,
+ // so we don't need to worry about sending DISABLE broadcasts to them.
+ N = mHosts.size();
+ for (int i = N - 1; i >= 0; i--) {
+ Host host = mHosts.get(i);
+ if (pkgName.equals(host.packageName)) {
+ deleteHostLocked(host);
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 31515e1..a7b08f5 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -1675,7 +1675,8 @@
synchronized(mClearDataLock) {
mClearingData = true;
try {
- mActivityManager.clearApplicationUserData(packageName, observer);
+ mActivityManager.clearApplicationUserData(packageName, observer,
+ Binder.getOrigCallingUser());
} catch (RemoteException e) {
// can't happen because the activity manager is in this process
}
diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java
index f475dd6..308661f 100644
--- a/services/java/com/android/server/NativeDaemonConnector.java
+++ b/services/java/com/android/server/NativeDaemonConnector.java
@@ -153,6 +153,10 @@
start = i + 1;
}
}
+ if (start == 0) {
+ final String rawEvent = new String(buffer, start, count, Charsets.UTF_8);
+ log("RCV incomplete <- {" + rawEvent + "}");
+ }
// We should end at the amount we read. If not, compact then
// buffer and read again.
@@ -297,7 +301,11 @@
throws NativeDaemonConnectorException {
final ArrayList<NativeDaemonEvent> events = Lists.newArrayList();
- mResponseQueue.clear();
+ while (mResponseQueue.size() > 0) {
+ try {
+ log("ignoring {" + mResponseQueue.take() + "}");
+ } catch (Exception e) {}
+ }
final String sentCommand = sendCommandLocked(cmd, args);
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 5039294..34a8a02 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -45,6 +45,7 @@
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
+import android.os.UserId;
import android.os.Vibrator;
import android.provider.Settings;
import android.telephony.TelephonyManager;
@@ -1034,7 +1035,7 @@
try {
ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo(
pkg, 0);
- if (ai.uid != uid) {
+ if (!UserId.isSameApp(ai.uid, uid)) {
throw new SecurityException("Calling uid " + uid + " gave package"
+ pkg + " which is owned by uid " + ai.uid);
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 3a6a3de..6fd5c07 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -75,6 +75,7 @@
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -104,6 +105,7 @@
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserId;
import android.provider.Settings;
import android.util.EventLog;
import android.util.Pair;
@@ -111,6 +113,7 @@
import android.util.Log;
import android.util.PrintWriterPrinter;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import android.util.TimeUtils;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -135,6 +138,7 @@
import java.lang.IllegalStateException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@@ -149,7 +153,9 @@
public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
+ private static final String USER_DATA_DIR = "/data/user/";
static final String TAG = "ActivityManager";
+ static final String TAG_MU = "ActivityManagerServiceMU";
static final boolean DEBUG = false;
static final boolean localLOGV = DEBUG;
static final boolean DEBUG_SWITCH = localLOGV || false;
@@ -172,6 +178,7 @@
static final boolean DEBUG_CONFIGURATION = localLOGV || false;
static final boolean DEBUG_POWER = localLOGV || false;
static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false;
+ static final boolean DEBUG_MU = localLOGV || false;
static final boolean VALIDATE_TOKENS = false;
static final boolean SHOW_ACTIVITY_START_TIME = true;
@@ -781,7 +788,16 @@
r.curComponent = new ComponentName(
info.activityInfo.applicationInfo.packageName,
info.activityInfo.name);
+ if (r.callingUid != Process.SYSTEM_UID) {
+ info.activityInfo = getActivityInfoForUser(info.activityInfo, UserId
+ .getUserId(r.callingUid));
+ }
r.curReceiver = info.activityInfo;
+ if (DEBUG_MU && r.callingUid > UserId.PER_USER_RANGE) {
+ Slog.v(TAG_MU, "Updated broadcast record activity info for secondary user, "
+ + info.activityInfo + ", callingUid = " + r.callingUid + ", uid = "
+ + info.activityInfo.applicationInfo.uid);
+ }
// Broadcast is being executed, its package can't be stopped.
try {
@@ -1286,17 +1302,7 @@
final HashMap<String, ArrayList<Intent>> mStickyBroadcasts =
new HashMap<String, ArrayList<Intent>>();
- /**
- * All currently running services.
- */
- final HashMap<ComponentName, ServiceRecord> mServices =
- new HashMap<ComponentName, ServiceRecord>();
-
- /**
- * All currently running services indexed by the Intent used to start them.
- */
- final HashMap<Intent.FilterComparison, ServiceRecord> mServicesByIntent =
- new HashMap<Intent.FilterComparison, ServiceRecord>();
+ final ServiceMap mServiceMap = new ServiceMap();
/**
* All currently bound service connections. Keys are the IBinder of
@@ -1362,6 +1368,8 @@
final HashMap<ComponentName, ContentProviderRecord> mProvidersByClass
= new HashMap<ComponentName, ContentProviderRecord>();
+ final ProviderMap mProviderMap = new ProviderMap();
+
/**
* List of content providers who have clients waiting for them. The
* application is currently being launched and the provider will be
@@ -1392,6 +1400,7 @@
uid = _uid;
}
}
+
private static ThreadLocal<Identity> sCallerIdentity = new ThreadLocal<Identity>();
/**
@@ -1688,7 +1697,7 @@
}
broadcastIntentLocked(null, null, intent,
null, null, 0, null, null, null,
- false, false, MY_PID, Process.SYSTEM_UID);
+ false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
Dialog d = new AppNotRespondingDialog(ActivityManagerService.this,
mContext, proc, (ActivityRecord)data.get("activity"));
@@ -2587,6 +2596,7 @@
processName);
return procs != null ? procs.valueAt(0) : null;
}
+ // uid = applyUserId(uid);
ProcessRecord proc = mProcessNames.get(processName, uid);
return proc;
}
@@ -2716,6 +2726,7 @@
try {
int uid = app.info.uid;
+
int[] gids = null;
try {
gids = mContext.getPackageManager().getPackageGids(
@@ -2827,7 +2838,7 @@
}
}
- boolean startHomeActivityLocked() {
+ boolean startHomeActivityLocked(int userId) {
if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL
&& mTopAction == null) {
// We are running in factory test mode, but unable to find
@@ -2850,6 +2861,8 @@
aInfo.applicationInfo.packageName, aInfo.name));
// Don't do this if the home app is currently being
// instrumented.
+ aInfo = new ActivityInfo(aInfo);
+ aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
ProcessRecord app = getProcessRecordLocked(aInfo.processName,
aInfo.applicationInfo.uid);
if (app == null || app.instrumentationClass == null) {
@@ -2858,11 +2871,10 @@
null, null, 0, 0, 0, false, false, null);
}
}
-
-
+
return true;
}
-
+
/**
* Starts the "new version setup screen" if appropriate.
*/
@@ -3026,10 +3038,23 @@
int grantedMode, IBinder resultTo,
String resultWho, int requestCode, boolean onlyIfNeeded, boolean debug,
String profileFile, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
+ 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;
+ } else {
+ userId = Binder.getOrigCallingUser();
+ }
+ }
return mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
- grantedUriPermissions, grantedMode, resultTo, resultWho,
- requestCode, onlyIfNeeded, debug, profileFile, profileFd, autoStopProfiler,
- null, null);
+ grantedUriPermissions, grantedMode, resultTo, resultWho, requestCode, onlyIfNeeded,
+ debug, profileFile, profileFd, autoStopProfiler, null, null, userId);
}
public final WaitResult startActivityAndWait(IApplicationThread caller,
@@ -3038,10 +3063,11 @@
String resultWho, int requestCode, boolean onlyIfNeeded, boolean debug,
String profileFile, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
WaitResult res = new WaitResult();
+ int userId = Binder.getOrigCallingUser();
mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
grantedUriPermissions, grantedMode, resultTo, resultWho,
requestCode, onlyIfNeeded, debug, profileFile, profileFd, autoStopProfiler,
- res, null);
+ res, null, userId);
return res;
}
@@ -3050,9 +3076,11 @@
int grantedMode, IBinder resultTo,
String resultWho, int requestCode, boolean onlyIfNeeded,
boolean debug, Configuration config) {
- return mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
+ int ret = mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
grantedUriPermissions, grantedMode, resultTo, resultWho,
- requestCode, onlyIfNeeded, debug, null, null, false, null, config);
+ requestCode, onlyIfNeeded,
+ debug, null, null, false, null, config, Binder.getOrigCallingUser());
+ return ret;
}
public int startActivityIntentSender(IApplicationThread caller,
@@ -3080,9 +3108,9 @@
mAppSwitchesAllowedTime = 0;
}
}
-
- return pir.sendInner(0, fillInIntent, resolvedType, null,
+ int ret = pir.sendInner(0, fillInIntent, resolvedType, null,
null, resultTo, resultWho, requestCode, flagsMask, flagsValues);
+ return ret;
}
public boolean startNextMatchingActivity(IBinder callingActivity,
@@ -3185,20 +3213,24 @@
// This is so super not safe, that only the system (or okay root)
// can do it.
+ int userId = Binder.getOrigCallingUser();
final int callingUid = Binder.getCallingUid();
if (callingUid != 0 && callingUid != Process.myUid()) {
throw new SecurityException(
"startActivityInPackage only available to the system");
}
- return mMainStack.startActivityMayWait(null, uid, intent, resolvedType,
+ int ret = mMainStack.startActivityMayWait(null, uid, intent, resolvedType,
null, 0, resultTo, resultWho, requestCode, onlyIfNeeded, false,
- null, null, false, null, null);
+ null, null, false, null, null, userId);
+ return ret;
}
public final int startActivities(IApplicationThread caller,
Intent[] intents, String[] resolvedTypes, IBinder resultTo) {
- return mMainStack.startActivities(caller, -1, intents, resolvedTypes, resultTo);
+ int ret = mMainStack.startActivities(caller, -1, intents, resolvedTypes, resultTo,
+ Binder.getOrigCallingUser());
+ return ret;
}
public final int startActivitiesInPackage(int uid,
@@ -3211,8 +3243,9 @@
throw new SecurityException(
"startActivityInPackage only available to the system");
}
-
- return mMainStack.startActivities(null, uid, intents, resolvedTypes, resultTo);
+ int ret = mMainStack.startActivities(null, uid, intents, resolvedTypes, resultTo,
+ UserId.getUserId(uid));
+ return ret;
}
final void addRecentTaskLocked(TaskRecord task) {
@@ -3224,8 +3257,9 @@
// Remove any existing entries that are the same kind of task.
for (int i=0; i<N; i++) {
TaskRecord tr = mRecentTasks.get(i);
- if ((task.affinity != null && task.affinity.equals(tr.affinity))
- || (task.intent != null && task.intent.filterEquals(tr.intent))) {
+ if (task.userId == tr.userId
+ && ((task.affinity != null && task.affinity.equals(tr.affinity))
+ || (task.intent != null && task.intent.filterEquals(tr.intent)))) {
mRecentTasks.remove(i);
i--;
N--;
@@ -3584,7 +3618,6 @@
private final int getLRURecordIndexForAppLocked(IApplicationThread thread) {
IBinder threadBinder = thread.asBinder();
-
// Find the application record.
for (int i=mLruProcesses.size()-1; i>=0; i--) {
ProcessRecord rec = mLruProcesses.get(i);
@@ -3961,7 +3994,7 @@
}
public boolean clearApplicationUserData(final String packageName,
- final IPackageDataObserver observer) {
+ final IPackageDataObserver observer, final int userId) {
int uid = Binder.getCallingUid();
int pid = Binder.getCallingPid();
long callingId = Binder.clearCallingIdentity();
@@ -3996,7 +4029,7 @@
Uri.fromParts("package", packageName, null));
intent.putExtra(Intent.EXTRA_UID, pkgUid);
broadcastIntentInPackage("android", Process.SYSTEM_UID, intent,
- null, null, 0, null, null, null, false, false);
+ null, null, 0, null, null, null, false, false, userId);
} catch (RemoteException e) {
}
} finally {
@@ -4091,7 +4124,7 @@
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
-
+ final int userId = Binder.getOrigCallingUser();
long callingId = Binder.clearCallingIdentity();
try {
IPackageManager pm = AppGlobals.getPackageManager();
@@ -4099,6 +4132,8 @@
synchronized(this) {
try {
pkgUid = pm.getPackageUid(packageName);
+ // Convert the uid to the one for the calling user
+ pkgUid = UserId.getUid(userId, pkgUid);
} catch (RemoteException e) {
}
if (pkgUid == -1) {
@@ -4181,7 +4216,7 @@
}
broadcastIntentLocked(null, null, intent, null,
- null, 0, null, null, null, false, false, -1, uid);
+ null, 0, null, null, null, false, false, -1, uid, 0 /* TODO: Verify */);
}
Binder.restoreCallingIdentity(origId);
}
@@ -4241,7 +4276,8 @@
intent.putExtra(Intent.EXTRA_UID, uid);
broadcastIntentLocked(null, null, intent,
null, null, 0, null, null, null,
- false, false, MY_PID, Process.SYSTEM_UID);
+ false, false,
+ MY_PID, Process.SYSTEM_UID, UserId.getUserId(uid));
}
private final boolean killPackageProcessesLocked(String packageName, int uid,
@@ -4345,7 +4381,8 @@
}
ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
- for (ServiceRecord service : mServices.values()) {
+ int userId = UserId.getUserId(uid);
+ for (ServiceRecord service : mServiceMap.getAllServices(userId)) {
if (service.packageName.equals(name)
&& (service.app == null || evenPersistent || !service.app.persistent)) {
if (!doit) {
@@ -4809,11 +4846,12 @@
// Tell anyone interested that we are done booting!
SystemProperties.set("sys.boot_completed", "1");
SystemProperties.set("dev.bootcomplete", "1");
+ /* TODO: Send this to all users that are to be logged in on startup */
broadcastIntentLocked(null, null,
new Intent(Intent.ACTION_BOOT_COMPLETED, null),
null, null, 0, null, null,
android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
- false, false, MY_PID, Process.SYSTEM_UID);
+ false, false, MY_PID, Process.SYSTEM_UID, Binder.getOrigCallingUser());
}
}
}
@@ -4954,7 +4992,7 @@
if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
int uid = AppGlobals.getPackageManager()
.getPackageUid(packageName);
- if (uid != Binder.getCallingUid()) {
+ if (UserId.getAppId(callingUid) != uid) {
String msg = "Permission Denial: getIntentSender() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
@@ -4965,7 +5003,10 @@
}
}
- return getIntentSenderLocked(type, packageName, callingUid,
+ if (DEBUG_MU)
+ Slog.i(TAG_MU, "Getting intent sender for origCallingUid="
+ + Binder.getOrigCallingUid());
+ return getIntentSenderLocked(type, packageName, Binder.getOrigCallingUid(),
token, resultWho, requestCode, intents, resolvedTypes, flags);
} catch (RemoteException e) {
@@ -4977,6 +5018,8 @@
IIntentSender getIntentSenderLocked(int type,
String packageName, int callingUid, IBinder token, String resultWho,
int requestCode, Intent[] intents, String[] resolvedTypes, int flags) {
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "getIntentSenderLocked(): uid=" + callingUid);
ActivityRecord activity = null;
if (type == INTENT_SENDER_ACTIVITY_RESULT) {
activity = mMainStack.isInStackLocked(token);
@@ -5220,7 +5263,7 @@
}
// If there is a uid that owns whatever is being accessed, it has
// blanket access to it regardless of the permissions it requires.
- if (owningUid >= 0 && uid == owningUid) {
+ if (owningUid >= 0 && UserId.isSameApp(uid, owningUid)) {
return PackageManager.PERMISSION_GRANTED;
}
// If the target is not exported, then nobody else can get to it.
@@ -5254,7 +5297,7 @@
if (permission == null) {
return PackageManager.PERMISSION_DENIED;
}
- return checkComponentPermission(permission, pid, uid, -1, true);
+ return checkComponentPermission(permission, pid, UserId.getAppId(uid), -1, true);
}
/**
@@ -5264,7 +5307,7 @@
int checkCallingPermission(String permission) {
return checkPermission(permission,
Binder.getCallingPid(),
- Binder.getCallingUid());
+ UserId.getAppId(Binder.getCallingUid()));
}
/**
@@ -5378,6 +5421,7 @@
pid = tlsIdentity.pid;
}
+ uid = UserId.getAppId(uid);
// Our own process gets to do everything.
if (pid == MY_PID) {
return PackageManager.PERMISSION_GRANTED;
@@ -5420,7 +5464,8 @@
String name = uri.getAuthority();
ProviderInfo pi = null;
- ContentProviderRecord cpr = mProvidersByName.get(name);
+ ContentProviderRecord cpr = mProviderMap.getProviderByName(name,
+ UserId.getUserId(callingUid));
if (cpr != null) {
pi = cpr.info;
} else {
@@ -5676,7 +5721,8 @@
final String authority = uri.getAuthority();
ProviderInfo pi = null;
- ContentProviderRecord cpr = mProvidersByName.get(authority);
+ ContentProviderRecord cpr = mProviderMap.getProviderByName(authority,
+ UserId.getUserId(callingUid));
if (cpr != null) {
pi = cpr.info;
} else {
@@ -5770,7 +5816,8 @@
final String authority = uri.getAuthority();
ProviderInfo pi = null;
- ContentProviderRecord cpr = mProvidersByName.get(authority);
+ ContentProviderRecord cpr = mProviderMap.getProviderByName(authority,
+ UserId.getUserId(r.info.uid));
if (cpr != null) {
pi = cpr.info;
} else {
@@ -6009,6 +6056,12 @@
public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum,
int flags) {
+ 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);
synchronized (this) {
enforceCallingPermission(android.Manifest.permission.GET_TASKS,
"getRecentTasks()");
@@ -6021,11 +6074,14 @@
maxNum < N ? maxNum : N);
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;
// Return the entry if desired by the caller. We always return
// the first entry, because callers always expect this to be the
- // forground app. We may filter others if the caller has
+ // foreground app. We may filter others if the caller has
// not supplied RECENT_WITH_EXCLUDED and there is some reason
// we should exclude the entry.
+
if (i == 0
|| ((flags&ActivityManager.RECENT_WITH_EXCLUDED) != 0)
|| (tr.intent == null)
@@ -6114,7 +6170,7 @@
// Find any running services associated with this app.
ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
- for (ServiceRecord sr : mServices.values()) {
+ for (ServiceRecord sr : mServiceMap.getAllServices(root.userId)) {
if (sr.packageName.equals(component.getPackageName())) {
services.add(sr);
}
@@ -6485,17 +6541,23 @@
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS);
} catch (RemoteException ex) {
}
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "generateApplicationProvidersLocked, app.info.uid = " + app.info.uid);
+ int userId = UserId.getUserId(app.info.uid);
if (providers != null) {
final int N = providers.size();
for (int i=0; i<N; i++) {
ProviderInfo cpi =
(ProviderInfo)providers.get(i);
+
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
- ContentProviderRecord cpr = mProvidersByClass.get(comp);
+ ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, userId);
if (cpr == null) {
cpr = new ContentProviderRecord(cpi, app.info, comp);
- mProvidersByClass.put(comp, cpr);
+ mProviderMap.putProviderByClass(comp, cpr);
}
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "generateApplicationProvidersLocked, cpi.uid = " + cpr.uid);
app.pubProviders.put(cpi.name, cpr);
app.addPackage(cpi.applicationInfo.packageName);
ensurePackageDexOpt(cpi.applicationInfo.packageName);
@@ -6605,8 +6667,8 @@
return false;
}
- private final ContentProviderHolder getContentProviderImpl(
- IApplicationThread caller, String name) {
+ private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
+ String name) {
ContentProviderRecord cpr;
ProviderInfo cpi = null;
@@ -6623,7 +6685,8 @@
}
// First check if this content provider has been published...
- cpr = mProvidersByName.get(name);
+ int userId = UserId.getUserId(r != null ? r.info.uid : Binder.getCallingUid());
+ cpr = mProviderMap.getProviderByName(name, userId);
boolean providerRunning = cpr != null;
if (providerRunning) {
cpi = cpr.info;
@@ -6708,6 +6771,9 @@
return null;
}
+ cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo,
+ Binder.getOrigCallingUser());
+
String msg;
if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) {
throw new SecurityException(msg);
@@ -6723,7 +6789,7 @@
}
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
- cpr = mProvidersByClass.get(comp);
+ cpr = mProviderMap.getProviderByClass(comp, Binder.getOrigCallingUser());
final boolean firstClass = cpr == null;
if (firstClass) {
try {
@@ -6737,6 +6803,7 @@
+ cpi.name);
return null;
}
+ ai = getAppInfoForUser(ai, Binder.getOrigCallingUser());
cpr = new ContentProviderRecord(cpi, ai, comp);
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
@@ -6805,9 +6872,9 @@
// Make sure the provider is published (the same provider class
// may be published under multiple names).
if (firstClass) {
- mProvidersByClass.put(comp, cpr);
+ mProviderMap.putProviderByClass(comp, cpr);
}
- mProvidersByName.put(name, cpr);
+ mProviderMap.putProviderByName(name, cpr);
incProviderCount(r, cpr);
}
}
@@ -6826,6 +6893,10 @@
return null;
}
try {
+ if (DEBUG_MU) {
+ Slog.v(TAG_MU, "Waiting to start provider " + cpr + " launchingApp="
+ + cpr.launchingApp);
+ }
cpr.wait();
} catch (InterruptedException ex) {
}
@@ -6843,7 +6914,8 @@
throw new SecurityException(msg);
}
- return getContentProviderImpl(caller, name);
+ ContentProviderHolder contentProvider = getContentProviderImpl(caller, name);
+ return contentProvider;
}
private ContentProviderHolder getContentProviderExternal(String name) {
@@ -6856,7 +6928,8 @@
*/
public void removeContentProvider(IApplicationThread caller, String name) {
synchronized (this) {
- ContentProviderRecord cpr = mProvidersByName.get(name);
+ int userId = UserId.getUserId(Binder.getCallingUid());
+ ContentProviderRecord cpr = mProviderMap.getProviderByName(name, userId);
if(cpr == null) {
// remove from mProvidersByClass
if (DEBUG_PROVIDER) Slog.v(TAG, name +
@@ -6871,8 +6944,11 @@
}
//update content provider record entry info
ComponentName comp = new ComponentName(cpr.info.packageName, cpr.info.name);
- ContentProviderRecord localCpr = mProvidersByClass.get(comp);
- if (localCpr.proc == r) {
+ ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp, userId);
+ if (DEBUG_PROVIDER) Slog.v(TAG, "Removing provider requested by "
+ + r.info.processName + " from process "
+ + localCpr.appInfo.processName);
+ if (localCpr.launchingApp == r) {
//should not happen. taken care of as a local provider
Slog.w(TAG, "removeContentProvider called on local provider: "
+ cpr.info.name + " in process " + r.processName);
@@ -6887,7 +6963,8 @@
private void removeContentProviderExternal(String name) {
synchronized (this) {
- ContentProviderRecord cpr = mProvidersByName.get(name);
+ ContentProviderRecord cpr = mProviderMap.getProviderByName(name,
+ Binder.getOrigCallingUser());
if(cpr == null) {
//remove from mProvidersByClass
if(localLOGV) Slog.v(TAG, name+" content provider not found in providers list");
@@ -6896,7 +6973,8 @@
//update content provider record entry info
ComponentName comp = new ComponentName(cpr.info.packageName, cpr.info.name);
- ContentProviderRecord localCpr = mProvidersByClass.get(comp);
+ ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp,
+ Binder.getOrigCallingUser());
localCpr.externals--;
if (localCpr.externals < 0) {
Slog.e(TAG, "Externals < 0 for content provider " + localCpr);
@@ -6913,6 +6991,8 @@
synchronized(this) {
final ProcessRecord r = getRecordForAppLocked(caller);
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "ProcessRecord uid = " + r.info.uid);
if (r == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
@@ -6929,12 +7009,14 @@
continue;
}
ContentProviderRecord dst = r.pubProviders.get(src.info.name);
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid);
if (dst != null) {
ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
- mProvidersByClass.put(comp, dst);
+ mProviderMap.putProviderByClass(comp, dst);
String names[] = dst.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
- mProvidersByName.put(names[j], dst);
+ mProviderMap.putProviderByName(names[j], dst);
}
int NL = mLaunchingProviders.size();
@@ -7679,8 +7761,10 @@
};
}
Slog.i(TAG, "Sending system update to: " + intent.getComponent());
+ /* TODO: Send this to all users */
broadcastIntentLocked(null, null, intent, null, finisher,
- 0, null, null, null, true, false, MY_PID, Process.SYSTEM_UID);
+ 0, null, null, null, true, false, MY_PID, Process.SYSTEM_UID,
+ Process.SYSTEM_UID);
if (finisher != null) {
mWaitingUpdate = true;
}
@@ -9306,8 +9390,14 @@
if ("all".equals(name)) {
synchronized (this) {
- for (ServiceRecord r1 : mServices.values()) {
- services.add(r1);
+ try {
+ List<UserInfo> users = AppGlobals.getPackageManager().getUsers();
+ for (UserInfo user : users) {
+ for (ServiceRecord r1 : mServiceMap.getAllServices(user.id)) {
+ services.add(r1);
+ }
+ }
+ } catch (RemoteException re) {
}
}
} else {
@@ -9325,18 +9415,24 @@
}
synchronized (this) {
- for (ServiceRecord r1 : mServices.values()) {
- if (componentName != null) {
- if (r1.name.equals(componentName)) {
- services.add(r1);
+ try {
+ List<UserInfo> users = AppGlobals.getPackageManager().getUsers();
+ for (UserInfo user : users) {
+ for (ServiceRecord r1 : mServiceMap.getAllServices(user.id)) {
+ if (componentName != null) {
+ if (r1.name.equals(componentName)) {
+ services.add(r1);
+ }
+ } else if (name != null) {
+ if (r1.name.flattenToString().contains(name)) {
+ services.add(r1);
+ }
+ } else if (System.identityHashCode(r1) == objectId) {
+ services.add(r1);
+ }
}
- } else if (name != null) {
- if (r1.name.flattenToString().contains(name)) {
- services.add(r1);
- }
- } else if (System.identityHashCode(r1) == objectId) {
- services.add(r1);
}
+ } catch (RemoteException re) {
}
}
}
@@ -9778,75 +9874,87 @@
matcher.build(args, opti);
pw.println("ACTIVITY MANAGER SERVICES (dumpsys activity services)");
- if (mServices.size() > 0) {
- boolean printed = false;
- long nowReal = SystemClock.elapsedRealtime();
- Iterator<ServiceRecord> it = mServices.values().iterator();
- needSep = false;
- while (it.hasNext()) {
- ServiceRecord r = it.next();
- if (!matcher.match(r, r.name)) {
- continue;
- }
- if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
- continue;
- }
- if (!printed) {
- pw.println(" Active services:");
- printed = true;
- }
- if (needSep) {
- pw.println();
- }
- pw.print(" * "); pw.println(r);
- if (dumpAll) {
- r.dump(pw, " ");
- needSep = true;
- } else {
- pw.print(" app="); pw.println(r.app);
- pw.print(" created=");
- TimeUtils.formatDuration(r.createTime, nowReal, pw);
- pw.print(" started="); pw.print(r.startRequested);
- pw.print(" connections="); pw.println(r.connections.size());
- if (r.connections.size() > 0) {
- pw.println(" Connections:");
- for (ArrayList<ConnectionRecord> clist : r.connections.values()) {
- for (int i=0; i<clist.size(); i++) {
- ConnectionRecord conn = clist.get(i);
- pw.print(" ");
- pw.print(conn.binding.intent.intent.getIntent().toShortString(
- false, false, false));
- pw.print(" -> ");
- ProcessRecord proc = conn.binding.client;
- pw.println(proc != null ? proc.toShortString() : "null");
+ try {
+ List<UserInfo> users = AppGlobals.getPackageManager().getUsers();
+ for (UserInfo user : users) {
+ if (mServiceMap.getAllServices(user.id).size() > 0) {
+ boolean printed = false;
+ long nowReal = SystemClock.elapsedRealtime();
+ Iterator<ServiceRecord> it = mServiceMap.getAllServices(
+ user.id).iterator();
+ needSep = false;
+ while (it.hasNext()) {
+ ServiceRecord r = it.next();
+ if (!matcher.match(r, r.name)) {
+ continue;
+ }
+ if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
+ continue;
+ }
+ if (!printed) {
+ pw.println(" Active services:");
+ printed = true;
+ }
+ if (needSep) {
+ pw.println();
+ }
+ pw.print(" * ");
+ pw.println(r);
+ if (dumpAll) {
+ r.dump(pw, " ");
+ needSep = true;
+ } else {
+ pw.print(" app=");
+ pw.println(r.app);
+ pw.print(" created=");
+ TimeUtils.formatDuration(r.createTime, nowReal, pw);
+ pw.print(" started=");
+ pw.print(r.startRequested);
+ pw.print(" connections=");
+ pw.println(r.connections.size());
+ if (r.connections.size() > 0) {
+ pw.println(" Connections:");
+ for (ArrayList<ConnectionRecord> clist : r.connections.values()) {
+ for (int i = 0; i < clist.size(); i++) {
+ ConnectionRecord conn = clist.get(i);
+ pw.print(" ");
+ pw.print(conn.binding.intent.intent.getIntent()
+ .toShortString(false, false, false));
+ pw.print(" -> ");
+ ProcessRecord proc = conn.binding.client;
+ pw.println(proc != null ? proc.toShortString() : "null");
+ }
+ }
}
}
- }
- }
- if (dumpClient && r.app != null && r.app.thread != null) {
- pw.println(" Client:");
- pw.flush();
- try {
- TransferPipe tp = new TransferPipe();
- try {
- r.app.thread.dumpService(
- tp.getWriteFd().getFileDescriptor(), r, args);
- tp.setBufferPrefix(" ");
- // Short timeout, since blocking here can
- // deadlock with the application.
- tp.go(fd, 2000);
- } finally {
- tp.kill();
+ if (dumpClient && r.app != null && r.app.thread != null) {
+ pw.println(" Client:");
+ pw.flush();
+ try {
+ TransferPipe tp = new TransferPipe();
+ try {
+ r.app.thread.dumpService(tp.getWriteFd().getFileDescriptor(),
+ r, args);
+ tp.setBufferPrefix(" ");
+ // Short timeout, since blocking here can
+ // deadlock with the application.
+ tp.go(fd, 2000);
+ } finally {
+ tp.kill();
+ }
+ } catch (IOException e) {
+ pw.println(" Failure while dumping the service: " + e);
+ } catch (RemoteException e) {
+ pw.println(" Got a RemoteException while dumping the service");
+ }
+ needSep = true;
}
- } catch (IOException e) {
- pw.println(" Failure while dumping the service: " + e);
- } catch (RemoteException e) {
- pw.println(" Got a RemoteException while dumping the service");
}
- needSep = true;
+ needSep = printed;
}
}
- needSep = printed;
+ } catch (RemoteException re) {
+
}
if (mPendingServices.size() > 0) {
@@ -9956,76 +10064,8 @@
matcher.build(args, opti);
pw.println("ACTIVITY MANAGER CONTENT PROVIDERS (dumpsys activity providers)");
- if (mProvidersByClass.size() > 0) {
- boolean printed = false;
- Iterator<Map.Entry<ComponentName, ContentProviderRecord>> it
- = mProvidersByClass.entrySet().iterator();
- while (it.hasNext()) {
- Map.Entry<ComponentName, ContentProviderRecord> e = it.next();
- ContentProviderRecord r = e.getValue();
- ComponentName comp = e.getKey();
- String cls = comp.getClassName();
- int end = cls.lastIndexOf('.');
- if (end > 0 && end < (cls.length()-2)) {
- cls = cls.substring(end+1);
- }
- if (!matcher.match(r, comp)) {
- continue;
- }
- if (dumpPackage != null && !dumpPackage.equals(comp.getPackageName())) {
- continue;
- }
- if (!printed) {
- if (needSep) pw.println(" ");
- needSep = true;
- pw.println(" Published content providers (by class):");
- printed = true;
- }
- pw.print(" * "); pw.print(cls); pw.print(" (");
- pw.print(comp.flattenToShortString()); pw.println(")");
- if (dumpAll) {
- r.dump(pw, " ");
- } else {
- if (r.proc != null) {
- pw.print(" "); pw.println(r.proc);
- } else {
- pw.println();
- }
- if (r.clients.size() > 0) {
- pw.println(" Clients:");
- for (ProcessRecord cproc : r.clients) {
- pw.print(" - "); pw.println(cproc);
- }
- }
- }
- }
- }
-
- if (dumpAll) {
- if (mProvidersByName.size() > 0) {
- boolean printed = false;
- Iterator<Map.Entry<String, ContentProviderRecord>> it
- = mProvidersByName.entrySet().iterator();
- while (it.hasNext()) {
- Map.Entry<String, ContentProviderRecord> e = it.next();
- ContentProviderRecord r = e.getValue();
- if (!matcher.match(r, r.name)) {
- continue;
- }
- if (dumpPackage != null && !dumpPackage.equals(r.name.getPackageName())) {
- continue;
- }
- if (!printed) {
- if (needSep) pw.println(" ");
- needSep = true;
- pw.println(" Authority to provider mappings:");
- printed = true;
- }
- pw.print(" "); pw.print(e.getKey()); pw.println(":");
- pw.print(" "); pw.println(r);
- }
- }
- }
+
+ mProviderMap.dumpProvidersLocked(pw, dumpAll);
if (mLaunchingProviders.size() > 0) {
boolean printed = false;
@@ -10911,10 +10951,10 @@
cpr.notifyAll();
}
- mProvidersByClass.remove(cpr.name);
+ mProviderMap.removeProviderByClass(cpr.name, UserId.getUserId(cpr.uid));
String names[] = cpr.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
- mProvidersByName.remove(names[j]);
+ mProviderMap.removeProviderByName(names[j], UserId.getUserId(cpr.uid));
}
Iterator<ProcessRecord> cit = cpr.clients.iterator();
@@ -11190,8 +11230,10 @@
ArrayList<ActivityManager.RunningServiceInfo> res
= new ArrayList<ActivityManager.RunningServiceInfo>();
- if (mServices.size() > 0) {
- Iterator<ServiceRecord> it = mServices.values().iterator();
+ int userId = UserId.getUserId(Binder.getCallingUid());
+ if (mServiceMap.getAllServices(userId).size() > 0) {
+ Iterator<ServiceRecord> it
+ = mServiceMap.getAllServices(userId).iterator();
while (it.hasNext() && res.size() < maxNum) {
res.add(makeRunningServiceInfoLocked(it.next()));
}
@@ -11211,7 +11253,8 @@
public PendingIntent getRunningServiceControlPanel(ComponentName name) {
synchronized (this) {
- ServiceRecord r = mServices.get(name);
+ int userId = UserId.getUserId(Binder.getCallingUid());
+ ServiceRecord r = mServiceMap.getServiceByName(name, userId);
if (r != null) {
for (ArrayList<ConnectionRecord> conn : r.connections.values()) {
for (int i=0; i<conn.size(); i++) {
@@ -11227,7 +11270,7 @@
private final ServiceRecord findServiceLocked(ComponentName name,
IBinder token) {
- ServiceRecord r = mServices.get(name);
+ ServiceRecord r = mServiceMap.getServiceByName(name, Binder.getOrigCallingUser());
return r == token ? r : null;
}
@@ -11245,11 +11288,11 @@
String resolvedType) {
ServiceRecord r = null;
if (service.getComponent() != null) {
- r = mServices.get(service.getComponent());
+ r = mServiceMap.getServiceByName(service.getComponent(), Binder.getOrigCallingUser());
}
if (r == null) {
Intent.FilterComparison filter = new Intent.FilterComparison(service);
- r = mServicesByIntent.get(filter);
+ r = mServiceMap.getServiceByIntent(filter, Binder.getOrigCallingUser());
}
if (r == null) {
@@ -11265,7 +11308,7 @@
ComponentName name = new ComponentName(
sInfo.applicationInfo.packageName, sInfo.name);
- r = mServices.get(name);
+ r = mServiceMap.getServiceByName(name, Binder.getOrigCallingUser());
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
}
@@ -11312,11 +11355,17 @@
private ServiceLookupResult retrieveServiceLocked(Intent service,
String resolvedType, int callingPid, int callingUid) {
ServiceRecord r = null;
+ if (DEBUG_SERVICE)
+ Slog.v(TAG, "retrieveServiceLocked: " + service + " type=" + resolvedType
+ + " origCallingUid=" + callingUid);
+
if (service.getComponent() != null) {
- r = mServices.get(service.getComponent());
+ r = mServiceMap.getServiceByName(service.getComponent(), Binder.getOrigCallingUser());
}
- Intent.FilterComparison filter = new Intent.FilterComparison(service);
- r = mServicesByIntent.get(filter);
+ if (r == null) {
+ Intent.FilterComparison filter = new Intent.FilterComparison(service);
+ r = mServiceMap.getServiceByIntent(filter, Binder.getOrigCallingUser());
+ }
if (r == null) {
try {
ResolveInfo rInfo =
@@ -11329,12 +11378,16 @@
": not found");
return null;
}
-
+ if (Binder.getOrigCallingUser() > 0) {
+ sInfo.applicationInfo = getAppInfoForUser(sInfo.applicationInfo,
+ Binder.getOrigCallingUser());
+ }
ComponentName name = new ComponentName(
sInfo.applicationInfo.packageName, sInfo.name);
- r = mServices.get(name);
+ r = mServiceMap.getServiceByName(name, Binder.getOrigCallingUser());
if (r == null) {
- filter = new Intent.FilterComparison(service.cloneFilter());
+ Intent.FilterComparison filter = new Intent.FilterComparison(
+ service.cloneFilter());
ServiceRestarter res = new ServiceRestarter();
BatteryStatsImpl.Uid.Pkg.Serv ss = null;
BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
@@ -11345,8 +11398,8 @@
}
r = new ServiceRecord(this, ss, name, filter, sInfo, res);
res.setService(r);
- mServices.put(name, r);
- mServicesByIntent.put(filter, r);
+ mServiceMap.putServiceByName(name, UserId.getUserId(r.appInfo.uid), r);
+ mServiceMap.putServiceByIntent(filter, UserId.getUserId(r.appInfo.uid), r);
// Make sure this component isn't in the pending list.
int N = mPendingServices.size();
@@ -11493,7 +11546,9 @@
if (app.thread == null) {
throw new RemoteException();
}
-
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "realStartServiceLocked, ServiceRecord.uid = " + r.appInfo.uid
+ + ", ProcessRecord.uid = " + app.info.uid);
r.app = app;
r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
@@ -11690,6 +11745,8 @@
final String appName = r.processName;
ProcessRecord app = getProcessRecordLocked(appName, r.appInfo.uid);
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid + " app=" + app);
if (app != null && app.thread != null) {
try {
app.addPackage(r.appInfo.packageName);
@@ -11794,8 +11851,8 @@
System.identityHashCode(r), r.shortName,
(r.app != null) ? r.app.pid : -1);
- mServices.remove(r.name);
- mServicesByIntent.remove(r.intent);
+ mServiceMap.removeServiceByName(r.name, r.userId);
+ mServiceMap.removeServiceByIntent(r.intent, r.userId);
r.totalRestartCount = 0;
unscheduleServiceRestartLocked(r);
@@ -11909,6 +11966,8 @@
throw new IllegalArgumentException("File descriptors passed in Intent");
}
+ if (DEBUG_SERVICE)
+ Slog.v(TAG, "startService: " + service + " type=" + resolvedType);
synchronized(this) {
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
@@ -11923,6 +11982,8 @@
ComponentName startServiceInPackage(int uid,
Intent service, String resolvedType) {
synchronized(this) {
+ if (DEBUG_SERVICE)
+ Slog.v(TAG, "startServiceInPackage: " + service + " type=" + resolvedType);
final long origId = Binder.clearCallingIdentity();
ComponentName res = startServiceLocked(null, service,
resolvedType, -1, uid);
@@ -12126,6 +12187,9 @@
if (DEBUG_SERVICE) Slog.v(TAG, "bindService: " + service
+ " type=" + resolvedType + " conn=" + connection.asBinder()
+ " flags=0x" + Integer.toHexString(flags));
+ if (DEBUG_MU)
+ Slog.i(TAG_MU, "bindService uid=" + Binder.getCallingUid() + " origUid="
+ + Binder.getOrigCallingUid());
final ProcessRecord callerApp = getRecordForAppLocked(caller);
if (callerApp == null) {
throw new SecurityException(
@@ -12168,7 +12232,7 @@
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType,
- Binder.getCallingPid(), Binder.getCallingUid());
+ Binder.getCallingPid(), Binder.getOrigCallingUid());
if (res == null) {
return 0;
}
@@ -12514,7 +12578,9 @@
r.callStart = false;
}
}
-
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "before serviceDontExecutingLocked, uid="
+ + Binder.getOrigCallingUid());
final long origId = Binder.clearCallingIdentity();
serviceDoneExecutingLocked(r, inStopping);
Binder.restoreCallingIdentity(origId);
@@ -12927,7 +12993,8 @@
String callerPackage, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle map, String requiredPermission,
- boolean ordered, boolean sticky, int callingPid, int callingUid) {
+ boolean ordered, boolean sticky, int callingPid, int callingUid,
+ int userId) {
intent = new Intent(intent);
// By default broadcasts do not go to stopped apps.
@@ -13102,10 +13169,11 @@
if (ai != null) {
receivers = new ArrayList();
ResolveInfo ri = new ResolveInfo();
- ri.activityInfo = ai;
+ ri.activityInfo = getActivityInfoForUser(ai, userId);
receivers.add(ri);
}
} else {
+ // TODO: Apply userId
// Need to resolve the intent to interested receivers...
if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
== 0) {
@@ -13274,7 +13342,7 @@
public final int broadcastIntent(IApplicationThread caller,
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle map,
- String requiredPermission, boolean serialized, boolean sticky) {
+ String requiredPermission, boolean serialized, boolean sticky, int userId) {
synchronized(this) {
intent = verifyBroadcastLocked(intent);
@@ -13285,8 +13353,8 @@
int res = broadcastIntentLocked(callerApp,
callerApp != null ? callerApp.info.packageName : null,
intent, resolvedType, resultTo,
- resultCode, resultData, map, requiredPermission, serialized,
- sticky, callingPid, callingUid);
+ resultCode, resultData, map, requiredPermission, serialized, sticky,
+ callingPid, callingUid, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
@@ -13295,21 +13363,21 @@
int broadcastIntentInPackage(String packageName, int uid,
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle map,
- String requiredPermission, boolean serialized, boolean sticky) {
+ String requiredPermission, boolean serialized, boolean sticky, int userId) {
synchronized(this) {
intent = verifyBroadcastLocked(intent);
final long origId = Binder.clearCallingIdentity();
int res = broadcastIntentLocked(null, packageName, intent, resolvedType,
resultTo, resultCode, resultData, map, requiredPermission,
- serialized, sticky, -1, uid);
+ serialized, sticky, -1, uid, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
}
- public final void unbroadcastIntent(IApplicationThread caller,
- Intent intent) {
+ // TODO: Use the userId; maybe mStickyBroadcasts need to be tied to the user.
+ public final void unbroadcastIntent(IApplicationThread caller, Intent intent, int userId) {
// Refuse possible leaked file descriptors
if (intent != null && intent.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -13514,7 +13582,6 @@
}
}
-
// =========================================================
// INSTRUMENTATION
// =========================================================
@@ -13786,12 +13853,12 @@
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_REPLACE_PENDING);
broadcastIntentLocked(null, null, intent, null, null, 0, null, null,
- null, false, false, MY_PID, Process.SYSTEM_UID);
+ null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
if ((changes&ActivityInfo.CONFIG_LOCALE) != 0) {
broadcastIntentLocked(null, null,
new Intent(Intent.ACTION_LOCALE_CHANGED),
null, null, 0, null, null,
- null, false, false, MY_PID, Process.SYSTEM_UID);
+ null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
}
}
}
@@ -15134,8 +15201,157 @@
// Multi-user methods
- public boolean switchUser(int userid) {
- // TODO
+ private int mCurrentUserId;
+ private SparseIntArray mLoggedInUsers = new SparseIntArray(5);
+
+ public boolean switchUser(int userId) {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != 0 && callingUid != Process.myUid()) {
+ Slog.e(TAG, "Trying to switch user from unauthorized app");
+ return false;
+ }
+ if (mCurrentUserId == userId)
+ return true;
+
+ synchronized (this) {
+ // Check if user is already logged in, otherwise check if user exists first before
+ // adding to the list of logged in users.
+ if (mLoggedInUsers.indexOfKey(userId) < 0) {
+ if (!userExists(userId)) {
+ return false;
+ }
+ mLoggedInUsers.append(userId, userId);
+ }
+
+ mCurrentUserId = userId;
+ boolean haveActivities = mMainStack.switchUser(userId);
+ if (!haveActivities) {
+ startHomeActivityLocked(userId);
+ }
+ }
return true;
}
+
+ private boolean userExists(int userId) {
+ try {
+ List<UserInfo> users = AppGlobals.getPackageManager().getUsers();
+ for (UserInfo user : users) {
+ if (user.id == userId) {
+ return true;
+ }
+ }
+ } catch (RemoteException re) {
+ // Won't happen, in same process
+ }
+
+ return false;
+ }
+
+
+ private int applyUserId(int uid, int userId) {
+ return UserId.getUid(userId, uid);
+ }
+
+ private ApplicationInfo getAppInfoForUser(ApplicationInfo info, int userId) {
+ ApplicationInfo newInfo = new ApplicationInfo(info);
+ newInfo.uid = applyUserId(info.uid, userId);
+ if (newInfo.uid >= Process.FIRST_APPLICATION_UID) {
+ newInfo.dataDir = USER_DATA_DIR + userId + "/"
+ + info.packageName;
+ }
+ return newInfo;
+ }
+
+ ActivityInfo getActivityInfoForUser(ActivityInfo aInfo, int userId) {
+ if (aInfo.applicationInfo.uid < Process.FIRST_APPLICATION_UID
+ || userId < 1) {
+ return aInfo;
+ }
+
+ ActivityInfo info = new ActivityInfo(aInfo);
+ info.applicationInfo = getAppInfoForUser(info.applicationInfo, userId);
+ return info;
+ }
+
+ static class ServiceMap {
+
+ private final SparseArray<HashMap<ComponentName, ServiceRecord>> mServicesByNamePerUser
+ = new SparseArray<HashMap<ComponentName, ServiceRecord>>();
+ private final SparseArray<HashMap<Intent.FilterComparison, ServiceRecord>>
+ mServicesByIntentPerUser = new SparseArray<
+ HashMap<Intent.FilterComparison, ServiceRecord>>();
+
+ ServiceRecord getServiceByName(ComponentName name, int callingUser) {
+ // TODO: Deal with global services
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "getServiceByName(" + name + "), callingUser = " + callingUser);
+ return getServices(callingUser).get(name);
+ }
+
+ ServiceRecord getServiceByName(ComponentName name) {
+ return getServiceByName(name, -1);
+ }
+
+ ServiceRecord getServiceByIntent(Intent.FilterComparison filter, int callingUser) {
+ // TODO: Deal with global services
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "getServiceByIntent(" + filter + "), callingUser = " + callingUser);
+ return getServicesByIntent(callingUser).get(filter);
+ }
+
+ ServiceRecord getServiceByIntent(Intent.FilterComparison filter) {
+ return getServiceByIntent(filter, -1);
+ }
+
+ void putServiceByName(ComponentName name, int callingUser, ServiceRecord value) {
+ // TODO: Deal with global services
+ getServices(callingUser).put(name, value);
+ }
+
+ void putServiceByIntent(Intent.FilterComparison filter, int callingUser,
+ ServiceRecord value) {
+ // TODO: Deal with global services
+ getServicesByIntent(callingUser).put(filter, value);
+ }
+
+ void removeServiceByName(ComponentName name, int callingUser) {
+ // TODO: Deal with global services
+ ServiceRecord removed = getServices(callingUser).remove(name);
+ if (DEBUG_MU)
+ Slog.v(TAG, "removeServiceByName user=" + callingUser + " name=" + name
+ + " removed=" + removed);
+ }
+
+ void removeServiceByIntent(Intent.FilterComparison filter, int callingUser) {
+ // TODO: Deal with global services
+ ServiceRecord removed = getServicesByIntent(callingUser).remove(filter);
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "removeServiceByIntent user=" + callingUser + " intent=" + filter
+ + " removed=" + removed);
+ }
+
+ Collection<ServiceRecord> getAllServices(int callingUser) {
+ // TODO: Deal with global services
+ return getServices(callingUser).values();
+ }
+
+ private HashMap<ComponentName, ServiceRecord> getServices(int callingUser) {
+ HashMap map = mServicesByNamePerUser.get(callingUser);
+ if (map == null) {
+ map = new HashMap<ComponentName, ServiceRecord>();
+ mServicesByNamePerUser.put(callingUser, map);
+ }
+ return map;
+ }
+
+ private HashMap<Intent.FilterComparison, ServiceRecord> getServicesByIntent(
+ int callingUser) {
+ HashMap map = mServicesByIntentPerUser.get(callingUser);
+ if (map == null) {
+ map = new HashMap<Intent.FilterComparison, ServiceRecord>();
+ mServicesByIntentPerUser.put(callingUser, map);
+ }
+ return map;
+ }
+ }
}
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index c819114..cdab6c6 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -25,6 +25,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.CompatibilityInfo;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.os.Build;
@@ -34,6 +35,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.UserId;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
@@ -55,6 +57,7 @@
final IApplicationToken.Stub appToken; // window manager token
final ActivityInfo info; // all about me
final int launchedFromUid; // always the uid who started the activity.
+ final int userId; // Which user is this running for?
final Intent intent; // the original intent that generated us
final ComponentName realActivity; // the intent component, or target of an alias.
final String shortComponentName; // the short component name of the intent
@@ -124,6 +127,7 @@
pw.print(prefix); pw.print("packageName="); pw.print(packageName);
pw.print(" processName="); pw.println(processName);
pw.print(prefix); pw.print("launchedFromUid="); pw.print(launchedFromUid);
+ pw.print(prefix); pw.print("userId="); pw.print(userId);
pw.print(" app="); pw.println(app);
pw.print(prefix); pw.println(intent.toInsecureString());
pw.print(prefix); pw.print("frontOfTask="); pw.print(frontOfTask);
@@ -281,6 +285,7 @@
appToken = new Token(this);
info = aInfo;
launchedFromUid = _launchedFromUid;
+ userId = UserId.getUserId(aInfo.applicationInfo.uid);
intent = _intent;
shortComponentName = _intent.getComponent().flattenToShortString();
resolvedType = _resolvedType;
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 6c11953..c3ae6a1 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -60,6 +60,7 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.UserId;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
@@ -274,6 +275,8 @@
int mThumbnailWidth = -1;
int mThumbnailHeight = -1;
+ private int mCurrentUser;
+
static final int SLEEP_TIMEOUT_MSG = 8;
static final int PAUSE_TIMEOUT_MSG = 9;
static final int IDLE_TIMEOUT_MSG = 10;
@@ -365,6 +368,7 @@
}
final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
+ // TODO: Don't look for any tasks from other users
int i = mHistory.size()-1;
while (i >= 0) {
ActivityRecord r = mHistory.get(i);
@@ -377,6 +381,7 @@
}
final ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
+ // TODO: Don't look for any tasks from other users
int i = mHistory.size()-1;
while (i >= 0) {
ActivityRecord r = mHistory.get(i);
@@ -398,6 +403,7 @@
* @return Returns the HistoryRecord of the next activity on the stack.
*/
final ActivityRecord topRunningActivityLocked(IBinder token, int taskId) {
+ // TODO: Don't look for any tasks from other users
int i = mHistory.size()-1;
while (i >= 0) {
ActivityRecord r = mHistory.get(i);
@@ -444,10 +450,11 @@
TaskRecord cp = null;
+ final int userId = UserId.getUserId(info.applicationInfo.uid);
final int N = mHistory.size();
for (int i=(N-1); i>=0; i--) {
ActivityRecord r = mHistory.get(i);
- if (!r.finishing && r.task != cp
+ if (!r.finishing && r.task != cp && r.userId == userId
&& r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
cp = r.task;
//Slog.i(TAG, "Comparing existing cls=" + r.task.intent.getComponent().flattenToShortString()
@@ -487,12 +494,13 @@
if (info.targetActivity != null) {
cls = new ComponentName(info.packageName, info.targetActivity);
}
+ final int userId = UserId.getUserId(info.applicationInfo.uid);
final int N = mHistory.size();
for (int i=(N-1); i>=0; i--) {
ActivityRecord r = mHistory.get(i);
if (!r.finishing) {
- if (r.intent.getComponent().equals(cls)) {
+ if (r.intent.getComponent().equals(cls) && r.userId == userId) {
//Slog.i(TAG, "Found matching class!");
//dump();
//Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
@@ -511,6 +519,43 @@
mService.mHandler.sendMessage(msg);
}
+ /*
+ * Move the activities around in the stack to bring a user to the foreground.
+ * @return whether there are any activities for the specified user.
+ */
+ final boolean switchUser(int userId) {
+ synchronized (mService) {
+ mCurrentUser = userId;
+
+ // Only one activity? Nothing to do...
+ if (mHistory.size() < 2)
+ return false;
+
+ boolean haveActivities = false;
+ // Check if the top activity is from the new user.
+ ActivityRecord top = mHistory.get(mHistory.size() - 1);
+ if (top.userId == userId) return true;
+ // Otherwise, move the user's activities to the top.
+ int N = mHistory.size();
+ int i = 0;
+ while (i < N) {
+ ActivityRecord r = mHistory.get(i);
+ if (r.userId == userId) {
+ ActivityRecord moveToTop = mHistory.remove(i);
+ mHistory.add(moveToTop);
+ // No need to check the top one now
+ N--;
+ haveActivities = true;
+ } else {
+ i++;
+ }
+ }
+ // Transition from the old top to the new top
+ resumeTopActivityLocked(top);
+ return haveActivities;
+ }
+ }
+
final boolean realStartActivityLocked(ActivityRecord r,
ProcessRecord app, boolean andResume, boolean checkConfig)
throws RemoteException {
@@ -1272,7 +1317,7 @@
// There are no more activities! Let's just start up the
// Launcher...
if (mMainStack) {
- return mService.startHomeActivityLocked();
+ return mService.startHomeActivityLocked(0);
}
}
@@ -1384,6 +1429,7 @@
// Launching this app's activity, make sure the app is no longer
// considered stopped.
try {
+ // TODO: Apply to the correct userId
AppGlobals.getPackageManager().setPackageStoppedState(
next.packageName, false);
} catch (RemoteException e1) {
@@ -2354,7 +2400,7 @@
}
}
}
-
+
ActivityRecord r = new ActivityRecord(mService, this, callerApp, callingUid,
intent, resolvedType, aInfo, mService.mConfiguration,
resultRecord, resultWho, requestCode, componentSpecified);
@@ -2420,7 +2466,8 @@
int grantedMode, boolean onlyIfNeeded, boolean doResume) {
final Intent intent = r.intent;
final int callingUid = r.launchedFromUid;
-
+ final int userId = r.userId;
+
int launchFlags = intent.getFlags();
// We'll invoke onUserLeaving before onPause only if the launching
@@ -2648,7 +2695,7 @@
// once.
ActivityRecord top = topRunningNonDelayedActivityLocked(notTop);
if (top != null && r.resultTo == null) {
- if (top.realActivity.equals(r.realActivity)) {
+ if (top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
if (top.app != null && top.app.thread != null) {
if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP
@@ -2821,12 +2868,12 @@
int grantedMode, IBinder resultTo,
String resultWho, int requestCode, boolean onlyIfNeeded,
boolean debug, String profileFile, ParcelFileDescriptor profileFd,
- boolean autoStopProfiler, WaitResult outResult, Configuration config) {
+ boolean autoStopProfiler,
+ WaitResult outResult, Configuration config, int userId) {
// Refuse possible leaked file descriptors
if (intent != null && intent.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
-
boolean componentSpecified = intent.getComponent() != null;
// Don't modify the client's object!
@@ -2835,6 +2882,7 @@
// Collect information about the target of the Intent.
ActivityInfo aInfo = resolveActivity(intent, resolvedType, debug,
profileFile, profileFd, autoStopProfiler);
+ aInfo = mService.getActivityInfoForUser(aInfo, userId);
synchronized (mService) {
int callingPid;
@@ -2915,6 +2963,7 @@
PackageManager.MATCH_DEFAULT_ONLY
| ActivityManagerService.STOCK_PM_FLAGS);
aInfo = rInfo != null ? rInfo.activityInfo : null;
+ aInfo = mService.getActivityInfoForUser(aInfo, userId);
} catch (RemoteException e) {
aInfo = null;
}
@@ -2977,7 +3026,8 @@
}
final int startActivities(IApplicationThread caller, int callingUid,
- Intent[] intents, String[] resolvedTypes, IBinder resultTo) {
+ Intent[] intents,
+ String[] resolvedTypes, IBinder resultTo, int userId) {
if (intents == null) {
throw new NullPointerException("intents is null");
}
@@ -3022,6 +3072,8 @@
// Collect information about the target of the Intent.
ActivityInfo aInfo = resolveActivity(intent, resolvedTypes[i], false,
null, null, false);
+ // TODO: New, check if this is correct
+ aInfo = mService.getActivityInfoForUser(aInfo, userId);
if (mMainStack && aInfo != null && (aInfo.applicationInfo.flags
& ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index abd2a1f..3b6a97c 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -24,6 +24,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserId;
import android.util.Slog;
import java.io.PrintWriter;
@@ -248,7 +249,8 @@
owner.broadcastIntentInPackage(key.packageName, uid,
finalIntent, resolvedType,
finishedReceiver, code, null, null,
- requiredPermission, (finishedReceiver != null), false);
+ requiredPermission, (finishedReceiver != null), false, UserId
+ .getUserId(uid));
sendFinish = false;
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
diff --git a/services/java/com/android/server/am/ProviderMap.java b/services/java/com/android/server/am/ProviderMap.java
new file mode 100644
index 0000000..44e7ecc
--- /dev/null
+++ b/services/java/com/android/server/am/ProviderMap.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Process;
+import android.os.UserId;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Keeps track of content providers by authority (name) and class. It separates the mapping by
+ * user and ones that are not user-specific (system providers).
+ */
+public class ProviderMap {
+
+ private static final String TAG = "ProviderMap";
+
+ private static final boolean DBG = false;
+
+ private final HashMap<String, ContentProviderRecord> mGlobalByName
+ = new HashMap<String, ContentProviderRecord>();
+ private final HashMap<ComponentName, ContentProviderRecord> mGlobalByClass
+ = new HashMap<ComponentName, ContentProviderRecord>();
+
+ private final SparseArray<HashMap<String, ContentProviderRecord>> mProvidersByNamePerUser
+ = new SparseArray<HashMap<String, ContentProviderRecord>>();
+ private final SparseArray<HashMap<ComponentName, ContentProviderRecord>> mProvidersByClassPerUser
+ = new SparseArray<HashMap<ComponentName, ContentProviderRecord>>();
+
+ ContentProviderRecord getProviderByName(String name) {
+ return getProviderByName(name, -1);
+ }
+
+ ContentProviderRecord getProviderByName(String name, int userId) {
+ if (DBG) {
+ Slog.i(TAG, "getProviderByName: " + name + " , callingUid = " + Binder.getCallingUid());
+ }
+ // Try to find it in the global list
+ ContentProviderRecord record = mGlobalByName.get(name);
+ if (record != null) {
+ return record;
+ }
+
+ // Check the current user's list
+ return getProvidersByName(userId).get(name);
+ }
+
+ ContentProviderRecord getProviderByClass(ComponentName name) {
+ return getProviderByClass(name, -1);
+ }
+
+ ContentProviderRecord getProviderByClass(ComponentName name, int userId) {
+ if (DBG) {
+ Slog.i(TAG, "getProviderByClass: " + name + ", callingUid = " + Binder.getCallingUid());
+ }
+ // Try to find it in the global list
+ ContentProviderRecord record = mGlobalByClass.get(name);
+ if (record != null) {
+ return record;
+ }
+
+ // Check the current user's list
+ return getProvidersByClass(userId).get(name);
+ }
+
+ void putProviderByName(String name, ContentProviderRecord record) {
+ if (DBG) {
+ 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);
+ } else {
+ final int userId = UserId.getUserId(record.appInfo.uid);
+ getProvidersByName(userId).put(name, record);
+ }
+ }
+
+ void putProviderByClass(ComponentName name, ContentProviderRecord record) {
+ if (DBG) {
+ 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);
+ } else {
+ final int userId = UserId.getUserId(record.appInfo.uid);
+ getProvidersByClass(userId).put(name, record);
+ }
+ }
+
+ void removeProviderByName(String name, int optionalUserId) {
+ if (mGlobalByName.containsKey(name)) {
+ if (DBG)
+ Slog.i(TAG, "Removing from globalByName name=" + name);
+ mGlobalByName.remove(name);
+ } else {
+ // TODO: Verify this works, i.e., the caller happens to be from the correct user
+ if (DBG)
+ Slog.i(TAG,
+ "Removing from providersByName name=" + name + " user="
+ + (optionalUserId == -1 ? Binder.getOrigCallingUser() : optionalUserId));
+ getProvidersByName(optionalUserId).remove(name);
+ }
+ }
+
+ void removeProviderByClass(ComponentName name, int optionalUserId) {
+ if (mGlobalByClass.containsKey(name)) {
+ if (DBG)
+ Slog.i(TAG, "Removing from globalByClass name=" + name);
+ mGlobalByClass.remove(name);
+ } else {
+ if (DBG)
+ Slog.i(TAG,
+ "Removing from providersByClass name=" + name + " user="
+ + (optionalUserId == -1 ? Binder.getOrigCallingUser() : optionalUserId));
+ getProvidersByClass(optionalUserId).remove(name);
+ }
+ }
+
+ private HashMap<String, ContentProviderRecord> getProvidersByName(int optionalUserId) {
+ final int userId = optionalUserId >= 0
+ ? optionalUserId : Binder.getOrigCallingUser();
+ final HashMap<String, ContentProviderRecord> map = mProvidersByNamePerUser.get(userId);
+ if (map == null) {
+ HashMap<String, ContentProviderRecord> newMap = new HashMap<String, ContentProviderRecord>();
+ mProvidersByNamePerUser.put(userId, newMap);
+ return newMap;
+ } else {
+ return map;
+ }
+ }
+
+ private HashMap<ComponentName, ContentProviderRecord> getProvidersByClass(int optionalUserId) {
+ final int userId = optionalUserId >= 0
+ ? optionalUserId : Binder.getOrigCallingUser();
+ final HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.get(userId);
+ if (map == null) {
+ HashMap<ComponentName, ContentProviderRecord> newMap = new HashMap<ComponentName, ContentProviderRecord>();
+ mProvidersByClassPerUser.put(userId, newMap);
+ return newMap;
+ } else {
+ return map;
+ }
+ }
+
+ private void dumpProvidersByClassLocked(PrintWriter pw, boolean dumpAll,
+ HashMap<ComponentName, ContentProviderRecord> map) {
+ Iterator<Map.Entry<ComponentName, ContentProviderRecord>> it = map.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<ComponentName, ContentProviderRecord> e = it.next();
+ ContentProviderRecord r = e.getValue();
+ if (dumpAll) {
+ pw.print(" * ");
+ pw.println(r);
+ r.dump(pw, " ");
+ } else {
+ pw.print(" * ");
+ pw.print(r.name.toShortString());
+ /*
+ if (r.app != null) {
+ pw.println(":");
+ pw.print(" ");
+ pw.println(r.app);
+ } else {
+ pw.println();
+ }
+ */
+ }
+ }
+ }
+
+ private void dumpProvidersByNameLocked(PrintWriter pw,
+ HashMap<String, ContentProviderRecord> map) {
+ Iterator<Map.Entry<String, ContentProviderRecord>> it = map.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<String, ContentProviderRecord> e = it.next();
+ ContentProviderRecord r = e.getValue();
+ pw.print(" ");
+ pw.print(e.getKey());
+ pw.print(": ");
+ pw.println(r);
+ }
+ }
+
+ 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);
+ pw.println(" ");
+ }
+
+ if (mProvidersByClassPerUser.size() > 1) {
+ 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);
+ dumpProvidersByClassLocked(pw, dumpAll, map);
+ }
+ needSep = true;
+
+ if (dumpAll) {
+ pw.println(" ");
+ pw.println(" Authority to provider mappings:");
+ dumpProvidersByNameLocked(pw, mGlobalByName);
+
+ for (int i = 0; i < mProvidersByNamePerUser.size(); i++) {
+ if (i > 0) {
+ pw.println(" User " + mProvidersByNamePerUser.keyAt(i) + ":");
+ }
+ dumpProvidersByNameLocked(pw, mProvidersByNamePerUser.valueAt(i));
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 257113b..75ba947 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -25,11 +25,13 @@
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.UserId;
import android.util.Slog;
import android.util.TimeUtils;
@@ -60,6 +62,7 @@
// all information about the service.
final ApplicationInfo appInfo;
// information about service's app.
+ final int userId; // user that this service is running as
final String packageName; // the package implementing intent's component
final String processName; // process where this component wants to run
final String permission;// permission needed to access service
@@ -289,6 +292,7 @@
this.restarter = restarter;
createTime = SystemClock.elapsedRealtime();
lastActivity = SystemClock.uptimeMillis();
+ userId = UserId.getUserId(appInfo.uid);
}
public AppBindRecord retrieveAppBindingLocked(Intent intent,
diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index de3129b..47ec218 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -19,7 +19,9 @@
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
import android.graphics.Bitmap;
+import android.os.UserId;
import java.io.PrintWriter;
@@ -37,6 +39,7 @@
boolean askedCompatMode;// Have asked the user about compat mode for this task.
String stringName; // caching of toString() result.
+ int userId; // user for which this task was created
TaskRecord(int _taskId, ActivityInfo info, Intent _intent) {
taskId = _taskId;
@@ -84,13 +87,17 @@
origActivity = new ComponentName(info.packageName, info.name);
}
}
-
+
if (intent != null &&
(intent.getFlags()&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
// Once we are set to an Intent with this flag, we count this
// task as having a true root activity.
rootWasReset = true;
}
+
+ if (info.applicationInfo != null) {
+ userId = UserId.getUserId(info.applicationInfo.uid);
+ }
}
void dump(PrintWriter pw, String prefix) {
@@ -154,6 +161,8 @@
} else {
sb.append(" ??");
}
+ sb.append(" U ");
+ sb.append(userId);
sb.append('}');
return stringName = sb.toString();
}
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index a71ccb5..9772d6a 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -48,6 +48,7 @@
import static android.net.NetworkTemplate.MATCH_MOBILE_ALL;
import static android.net.NetworkTemplate.MATCH_WIFI;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
+import static android.net.TrafficStats.MB_IN_BYTES;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
@@ -154,10 +155,7 @@
private static final int VERSION_ADDED_SNOOZE = 2;
private static final int VERSION_ADDED_RESTRICT_BACKGROUND = 3;
private static final int VERSION_ADDED_METERED = 4;
-
- private static final long KB_IN_BYTES = 1024;
- private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
- private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
+ private static final int VERSION_SPLIT_SNOOZE = 5;
// @VisibleForTesting
public static final int TYPE_WARNING = 0x1;
@@ -176,6 +174,8 @@
private static final String ATTR_WARNING_BYTES = "warningBytes";
private static final String ATTR_LIMIT_BYTES = "limitBytes";
private static final String ATTR_LAST_SNOOZE = "lastSnooze";
+ private static final String ATTR_LAST_WARNING_SNOOZE = "lastWarningSnooze";
+ private static final String ATTR_LAST_LIMIT_SNOOZE = "lastLimitSnooze";
private static final String ATTR_METERED = "metered";
private static final String ATTR_UID = "uid";
private static final String ATTR_POLICY = "policy";
@@ -184,7 +184,9 @@
// @VisibleForTesting
public static final String ACTION_ALLOW_BACKGROUND =
- "com.android.server.action.ACTION_ALLOW_BACKGROUND";
+ "com.android.server.net.action.ALLOW_BACKGROUND";
+ public static final String ACTION_SNOOZE_WARNING =
+ "com.android.server.net.action.SNOOZE_WARNING";
private static final long TIME_CACHE_MAX_AGE = DAY_IN_MILLIS;
@@ -333,6 +335,11 @@
final IntentFilter allowFilter = new IntentFilter(ACTION_ALLOW_BACKGROUND);
mContext.registerReceiver(mAllowReceiver, allowFilter, MANAGE_NETWORK_POLICY, mHandler);
+ // listen for snooze warning from notifications
+ final IntentFilter snoozeWarningFilter = new IntentFilter(ACTION_SNOOZE_WARNING);
+ mContext.registerReceiver(mSnoozeWarningReceiver, snoozeWarningFilter,
+ MANAGE_NETWORK_POLICY, mHandler);
+
}
private IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
@@ -418,6 +425,21 @@
};
/**
+ * Receiver that watches for {@link Notification} control of
+ * {@link NetworkPolicy#lastWarningSnooze}.
+ */
+ private BroadcastReceiver mSnoozeWarningReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // on background handler thread, and verified MANAGE_NETWORK_POLICY
+ // permission above.
+
+ final NetworkTemplate template = intent.getParcelableExtra(EXTRA_NETWORK_TEMPLATE);
+ performSnooze(template, TYPE_WARNING);
+ }
+ };
+
+ /**
* Observer that watches for {@link INetworkManagementService} alerts.
*/
private INetworkManagementEventObserver mAlertObserver = new NetworkAlertObserver() {
@@ -458,7 +480,7 @@
final long totalBytes = getTotalBytes(policy.template, start, end);
if (policy.isOverLimit(totalBytes)) {
- if (policy.lastSnooze >= start) {
+ if (policy.lastLimitSnooze >= start) {
enqueueNotification(policy, TYPE_LIMIT_SNOOZED, totalBytes);
} else {
enqueueNotification(policy, TYPE_LIMIT, totalBytes);
@@ -468,7 +490,7 @@
} else {
notifyUnderLimitLocked(policy.template);
- if (policy.warningBytes != WARNING_DISABLED && totalBytes >= policy.warningBytes) {
+ if (policy.isOverWarning(totalBytes) && policy.lastWarningSnooze < start) {
enqueueNotification(policy, TYPE_WARNING, totalBytes);
}
}
@@ -534,7 +556,7 @@
final String tag = buildNotificationTag(policy, type);
final Notification.Builder builder = new Notification.Builder(mContext);
builder.setOnlyAlertOnce(true);
- builder.setOngoing(true);
+ builder.setWhen(0L);
final Resources res = mContext.getResources();
switch (type) {
@@ -547,9 +569,14 @@
builder.setContentTitle(title);
builder.setContentText(body);
- final Intent intent = buildViewDataUsageIntent(policy.template);
+ final Intent snoozeIntent = buildSnoozeWarningIntent(policy.template);
+ builder.setDeleteIntent(PendingIntent.getBroadcast(
+ mContext, 0, snoozeIntent, PendingIntent.FLAG_UPDATE_CURRENT));
+
+ final Intent viewIntent = buildViewDataUsageIntent(policy.template);
builder.setContentIntent(PendingIntent.getActivity(
- mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
+ mContext, 0, viewIntent, PendingIntent.FLAG_UPDATE_CURRENT));
+
break;
}
case TYPE_LIMIT: {
@@ -574,6 +601,7 @@
break;
}
+ builder.setOngoing(true);
builder.setSmallIcon(R.drawable.stat_notify_disabled);
builder.setTicker(title);
builder.setContentTitle(title);
@@ -608,6 +636,7 @@
break;
}
+ builder.setOngoing(true);
builder.setSmallIcon(R.drawable.stat_notify_error);
builder.setTicker(title);
builder.setContentTitle(title);
@@ -720,10 +749,11 @@
final long totalBytes = getTotalBytes(policy.template, start, end);
// disable data connection when over limit and not snoozed
- final boolean overLimit = policy.isOverLimit(totalBytes) && policy.lastSnooze < start;
- final boolean enabled = !overLimit;
+ final boolean overLimitWithoutSnooze = policy.isOverLimit(totalBytes)
+ && policy.lastLimitSnooze < start;
+ final boolean networkEnabled = !overLimitWithoutSnooze;
- setNetworkTemplateEnabled(policy.template, enabled);
+ setNetworkTemplateEnabled(policy.template, networkEnabled);
}
}
@@ -827,7 +857,7 @@
// metered network, but no policy limit; we still need to
// restrict apps, so push really high quota.
quotaBytes = Long.MAX_VALUE;
- } else if (policy.lastSnooze >= start) {
+ } else if (policy.lastLimitSnooze >= start) {
// snoozing past quota, but we still need to restrict apps,
// so push really high quota.
quotaBytes = Long.MAX_VALUE;
@@ -896,8 +926,8 @@
final int cycleDay = time.monthDay;
final NetworkTemplate template = buildTemplateMobileAll(subscriberId);
- mNetworkPolicy.put(template, new NetworkPolicy(
- template, cycleDay, warningBytes, LIMIT_DISABLED, SNOOZE_NEVER, true));
+ mNetworkPolicy.put(template, new NetworkPolicy(template, cycleDay, warningBytes,
+ LIMIT_DISABLED, SNOOZE_NEVER, SNOOZE_NEVER, true));
writePolicyLocked();
}
}
@@ -935,11 +965,13 @@
final int cycleDay = readIntAttribute(in, ATTR_CYCLE_DAY);
final long warningBytes = readLongAttribute(in, ATTR_WARNING_BYTES);
final long limitBytes = readLongAttribute(in, ATTR_LIMIT_BYTES);
- final long lastSnooze;
- if (version >= VERSION_ADDED_SNOOZE) {
- lastSnooze = readLongAttribute(in, ATTR_LAST_SNOOZE);
+ final long lastLimitSnooze;
+ if (version >= VERSION_SPLIT_SNOOZE) {
+ lastLimitSnooze = readLongAttribute(in, ATTR_LAST_LIMIT_SNOOZE);
+ } else if (version >= VERSION_ADDED_SNOOZE) {
+ lastLimitSnooze = readLongAttribute(in, ATTR_LAST_SNOOZE);
} else {
- lastSnooze = SNOOZE_NEVER;
+ lastLimitSnooze = SNOOZE_NEVER;
}
final boolean metered;
if (version >= VERSION_ADDED_METERED) {
@@ -955,11 +987,18 @@
metered = false;
}
}
+ final long lastWarningSnooze;
+ if (version >= VERSION_SPLIT_SNOOZE) {
+ lastWarningSnooze = readLongAttribute(in, ATTR_LAST_WARNING_SNOOZE);
+ } else {
+ lastWarningSnooze = SNOOZE_NEVER;
+ }
final NetworkTemplate template = new NetworkTemplate(
networkTemplate, subscriberId);
- mNetworkPolicy.put(template, new NetworkPolicy(
- template, cycleDay, warningBytes, limitBytes, lastSnooze, metered));
+ mNetworkPolicy.put(template, new NetworkPolicy(template, cycleDay,
+ warningBytes, limitBytes, lastWarningSnooze, lastLimitSnooze,
+ metered));
} else if (TAG_UID_POLICY.equals(tag)) {
final int uid = readIntAttribute(in, ATTR_UID);
@@ -1014,7 +1053,7 @@
out.startDocument(null, true);
out.startTag(null, TAG_POLICY_LIST);
- writeIntAttribute(out, ATTR_VERSION, VERSION_ADDED_METERED);
+ writeIntAttribute(out, ATTR_VERSION, VERSION_SPLIT_SNOOZE);
writeBooleanAttribute(out, ATTR_RESTRICT_BACKGROUND, mRestrictBackground);
// write all known network policies
@@ -1030,7 +1069,8 @@
writeIntAttribute(out, ATTR_CYCLE_DAY, policy.cycleDay);
writeLongAttribute(out, ATTR_WARNING_BYTES, policy.warningBytes);
writeLongAttribute(out, ATTR_LIMIT_BYTES, policy.limitBytes);
- writeLongAttribute(out, ATTR_LAST_SNOOZE, policy.lastSnooze);
+ writeLongAttribute(out, ATTR_LAST_WARNING_SNOOZE, policy.lastWarningSnooze);
+ writeLongAttribute(out, ATTR_LAST_LIMIT_SNOOZE, policy.lastLimitSnooze);
writeBooleanAttribute(out, ATTR_METERED, policy.metered);
out.endTag(null, TAG_NETWORK_POLICY);
}
@@ -1141,9 +1181,12 @@
}
@Override
- public void snoozePolicy(NetworkTemplate template) {
+ public void snoozeLimit(NetworkTemplate template) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+ performSnooze(template, TYPE_LIMIT);
+ }
+ private void performSnooze(NetworkTemplate template, int type) {
maybeRefreshTrustedTime();
final long currentTime = currentTimeMillis();
synchronized (mRulesLock) {
@@ -1153,7 +1196,16 @@
throw new IllegalArgumentException("unable to find policy for " + template);
}
- policy.lastSnooze = currentTime;
+ switch (type) {
+ case TYPE_WARNING:
+ policy.lastWarningSnooze = currentTime;
+ break;
+ case TYPE_LIMIT:
+ policy.lastLimitSnooze = currentTime;
+ break;
+ default:
+ throw new IllegalArgumentException("unexpected type");
+ }
updateNetworkEnabledLocked();
updateNetworkRulesLocked();
@@ -1246,12 +1298,17 @@
}
synchronized (mRulesLock) {
- if (argSet.contains("unsnooze")) {
+ if (argSet.contains("--unsnooze")) {
for (NetworkPolicy policy : mNetworkPolicy.values()) {
- policy.lastSnooze = SNOOZE_NEVER;
+ policy.clearSnooze();
}
+
+ updateNetworkEnabledLocked();
+ updateNetworkRulesLocked();
+ updateNotificationsLocked();
writePolicyLocked();
- fout.println("Wiped snooze timestamps");
+
+ fout.println("Cleared snooze timestamps");
return;
}
@@ -1599,6 +1656,12 @@
return new Intent(ACTION_ALLOW_BACKGROUND);
}
+ private static Intent buildSnoozeWarningIntent(NetworkTemplate template) {
+ final Intent intent = new Intent(ACTION_SNOOZE_WARNING);
+ intent.putExtra(EXTRA_NETWORK_TEMPLATE, template);
+ return intent;
+ }
+
private static Intent buildNetworkOverLimitIntent(NetworkTemplate template) {
final Intent intent = new Intent();
intent.setComponent(new ComponentName(
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 7930caf..c9b67fc 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -34,6 +34,7 @@
import static android.net.NetworkStats.UID_ALL;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.NetworkTemplate.buildTemplateWifi;
+import static android.net.TrafficStats.MB_IN_BYTES;
import static android.provider.Settings.Secure.NETSTATS_DEV_BUCKET_DURATION;
import static android.provider.Settings.Secure.NETSTATS_DEV_DELETE_AGE;
import static android.provider.Settings.Secure.NETSTATS_DEV_PERSIST_BYTES;
@@ -121,6 +122,7 @@
private static final int MSG_PERFORM_POLL = 1;
private static final int MSG_UPDATE_IFACES = 2;
+ private static final int MSG_REGISTER_GLOBAL_ALERT = 3;
/** Flags to control detail level of poll event. */
private static final int FLAG_PERSIST_NETWORK = 0x1;
@@ -152,10 +154,6 @@
private PendingIntent mPollIntent;
- private static final long KB_IN_BYTES = 1024;
- private static final long MB_IN_BYTES = 1024 * KB_IN_BYTES;
- private static final long GB_IN_BYTES = 1024 * MB_IN_BYTES;
-
private static final String PREFIX_DEV = "dev";
private static final String PREFIX_UID = "uid";
private static final String PREFIX_UID_TAG = "uid_tag";
@@ -600,7 +598,7 @@
mHandler.obtainMessage(MSG_PERFORM_POLL, flags, 0).sendToTarget();
// re-arm global alert for next update
- registerGlobalAlert();
+ mHandler.obtainMessage(MSG_REGISTER_GLOBAL_ALERT).sendToTarget();
}
}
};
@@ -951,6 +949,10 @@
updateIfaces();
return true;
}
+ case MSG_REGISTER_GLOBAL_ALERT: {
+ registerGlobalAlert();
+ return true;
+ }
default: {
return false;
}
diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java
index 11ccd60..9b1973e 100644
--- a/services/java/com/android/server/pm/Installer.java
+++ b/services/java/com/android/server/pm/Installer.java
@@ -277,6 +277,27 @@
return execute(builder.toString());
}
+ /**
+ * Clone all the package data directories from srcUserId to targetUserId. If copyData is true,
+ * some of the data is also copied, otherwise just empty directories are created with the
+ * correct access rights.
+ * @param srcUserId user to copy the data directories from
+ * @param targetUserId user to copy the data directories to
+ * @param copyData whether the data itself is to be copied. If false, empty directories are
+ * created.
+ * @return success/error code
+ */
+ public int cloneUserData(int srcUserId, int targetUserId, boolean copyData) {
+ StringBuilder builder = new StringBuilder("cloneuserdata");
+ builder.append(' ');
+ builder.append(srcUserId);
+ builder.append(' ');
+ builder.append(targetUserId);
+ builder.append(' ');
+ builder.append(copyData ? '1' : '0');
+ return execute(builder.toString());
+ }
+
public boolean ping() {
if (execute("ping") < 0) {
return false;
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 090ca64..38c128c 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -92,6 +92,7 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserId;
import android.security.SystemKeyStore;
import android.util.DisplayMetrics;
import android.util.EventLog;
@@ -1743,12 +1744,16 @@
}
public ActivityInfo getActivityInfo(ComponentName component, int flags) {
+ return getActivityInfo(component, flags, Binder.getOrigCallingUser());
+ }
+
+ ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
synchronized (mPackages) {
PackageParser.Activity a = mActivities.mActivities.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
if (a != null && mSettings.isEnabledLPr(a.info, flags)) {
- return PackageParser.generateActivityInfo(a, flags);
+ return PackageParser.generateActivityInfo(a, flags, userId);
}
if (mResolveComponentName.equals(component)) {
return mResolveActivity;
@@ -1758,36 +1763,48 @@
}
public ActivityInfo getReceiverInfo(ComponentName component, int flags) {
+ return getReceiverInfo(component, flags, Binder.getOrigCallingUser());
+ }
+
+ ActivityInfo getReceiverInfo(ComponentName component, int flags, int userId) {
synchronized (mPackages) {
PackageParser.Activity a = mReceivers.mActivities.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getReceiverInfo " + component + ": " + a);
if (a != null && mSettings.isEnabledLPr(a.info, flags)) {
- return PackageParser.generateActivityInfo(a, flags);
+ return PackageParser.generateActivityInfo(a, flags, userId);
}
}
return null;
}
public ServiceInfo getServiceInfo(ComponentName component, int flags) {
+ return getServiceInfo(component, flags, Binder.getOrigCallingUser());
+ }
+
+ ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) {
synchronized (mPackages) {
PackageParser.Service s = mServices.mServices.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getServiceInfo " + component + ": " + s);
if (s != null && mSettings.isEnabledLPr(s.info, flags)) {
- return PackageParser.generateServiceInfo(s, flags);
+ return PackageParser.generateServiceInfo(s, flags, userId);
}
}
return null;
}
public ProviderInfo getProviderInfo(ComponentName component, int flags) {
+ return getProviderInfo(component, flags, UserId.getUserId(Binder.getCallingUid()));
+ }
+
+ ProviderInfo getProviderInfo(ComponentName component, int flags, int userId) {
synchronized (mPackages) {
PackageParser.Provider p = mProvidersByComponent.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getProviderInfo " + component + ": " + p);
if (p != null && mSettings.isEnabledLPr(p.info, flags)) {
- return PackageParser.generateProviderInfo(p, flags);
+ return PackageParser.generateProviderInfo(p, flags, userId);
}
}
return null;
@@ -1850,7 +1867,7 @@
public int checkUidPermission(String permName, int uid) {
synchronized (mPackages) {
- Object obj = mSettings.getUserIdLPr(uid);
+ Object obj = mSettings.getUserIdLPr(UserId.getAppId(uid));
if (obj != null) {
GrantedPermissions gp = (GrantedPermissions)obj;
if (gp.grantedPermissions.contains(permName)) {
@@ -1881,7 +1898,7 @@
if (permName != null) {
BasePermission bp = findPermissionTreeLP(permName);
if (bp != null) {
- if (bp.uid == Binder.getCallingUid()) {
+ if (bp.uid == UserId.getAppId(Binder.getCallingUid())) {
return bp;
}
throw new SecurityException("Calling uid "
@@ -2010,6 +2027,9 @@
}
public int checkUidSignatures(int uid1, int uid2) {
+ // Map to base uids.
+ uid1 = UserId.getAppId(uid1);
+ uid2 = UserId.getAppId(uid2);
// reader
synchronized (mPackages) {
Signature[] s1;
@@ -2067,6 +2087,7 @@
}
public String[] getPackagesForUid(int uid) {
+ uid = UserId.getAppId(uid);
// reader
synchronized (mPackages) {
Object obj = mSettings.getUserIdLPr(uid);
@@ -2091,7 +2112,7 @@
public String getNameForUid(int uid) {
// reader
synchronized (mPackages) {
- Object obj = mSettings.getUserIdLPr(uid);
+ Object obj = mSettings.getUserIdLPr(UserId.getAppId(uid));
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
return sus.name + ":" + sus.userId;
@@ -2110,7 +2131,7 @@
// reader
synchronized (mPackages) {
final SharedUserSetting suid = mSettings.getSharedUserLPw(sharedUserName, 0, false);
- if(suid == null) {
+ if (suid == null) {
return -1;
}
return suid.userId;
@@ -2252,6 +2273,9 @@
comp = intent.getComponent();
}
}
+
+ final int userId = UserId.getUserId(Binder.getCallingUid());
+
if (comp != null) {
final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
final ActivityInfo ai = getActivityInfo(comp, flags);
@@ -2603,6 +2627,7 @@
Arrays.sort(keys);
int i = getContinuationPoint(keys, lastRead);
final int N = keys.length;
+ final int userId = UserId.getUserId(Binder.getCallingUid());
while (i < N) {
final String packageName = keys[i++];
@@ -2616,7 +2641,7 @@
} else {
final PackageParser.Package p = mPackages.get(packageName);
if (p != null) {
- ai = PackageParser.generateApplicationInfo(p, flags);
+ ai = PackageParser.generateApplicationInfo(p, flags, userId);
}
}
@@ -2639,12 +2664,13 @@
// reader
synchronized (mPackages) {
final Iterator<PackageParser.Package> i = mPackages.values().iterator();
+ final int userId = UserId.getUserId(Binder.getCallingUid());
while (i.hasNext()) {
final PackageParser.Package p = i.next();
if (p.applicationInfo != null
&& (p.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) != 0
&& (!mSafeMode || isSystemApp(p))) {
- finalList.add(PackageParser.generateApplicationInfo(p, flags));
+ finalList.add(PackageParser.generateApplicationInfo(p, flags, userId));
}
}
}
@@ -2660,7 +2686,8 @@
&& mSettings.isEnabledLPr(provider.info, flags)
&& (!mSafeMode || (provider.info.applicationInfo.flags
&ApplicationInfo.FLAG_SYSTEM) != 0)
- ? PackageParser.generateProviderInfo(provider, flags)
+ ? PackageParser.generateProviderInfo(provider, flags,
+ UserId.getUserId(Binder.getCallingUid()))
: null;
}
}
@@ -2674,7 +2701,7 @@
synchronized (mPackages) {
final Iterator<Map.Entry<String, PackageParser.Provider>> i = mProviders.entrySet()
.iterator();
-
+ final int userId = UserId.getUserId(Binder.getCallingUid());
while (i.hasNext()) {
Map.Entry<String, PackageParser.Provider> entry = i.next();
PackageParser.Provider p = entry.getValue();
@@ -2683,7 +2710,7 @@
&& (!mSafeMode || (p.info.applicationInfo.flags
&ApplicationInfo.FLAG_SYSTEM) != 0)) {
outNames.add(entry.getKey());
- outInfo.add(PackageParser.generateProviderInfo(p, 0));
+ outInfo.add(PackageParser.generateProviderInfo(p, 0, userId));
}
}
}
@@ -2696,19 +2723,21 @@
// reader
synchronized (mPackages) {
final Iterator<PackageParser.Provider> i = mProvidersByComponent.values().iterator();
+ final int userId = UserId.getUserId(Binder.getCallingUid());
while (i.hasNext()) {
final PackageParser.Provider p = i.next();
if (p.info.authority != null
&& (processName == null
|| (p.info.processName.equals(processName)
- && p.info.applicationInfo.uid == uid))
+ && UserId.getAppId(p.info.applicationInfo.uid)
+ == UserId.getAppId(uid)))
&& mSettings.isEnabledLPr(p.info, flags)
&& (!mSafeMode
|| (p.info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)) {
if (finalList == null) {
finalList = new ArrayList<ProviderInfo>(3);
}
- finalList.add(PackageParser.generateProviderInfo(p, flags));
+ finalList.add(PackageParser.generateProviderInfo(p, flags, userId));
}
}
}
@@ -4461,8 +4490,8 @@
return null;
}
final ResolveInfo res = new ResolveInfo();
- res.activityInfo = PackageParser.generateActivityInfo(activity,
- mFlags);
+ res.activityInfo = PackageParser.generateActivityInfo(activity, mFlags,
+ Binder.getOrigCallingUser());
if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
res.filter = info;
}
@@ -4637,8 +4666,8 @@
return null;
}
final ResolveInfo res = new ResolveInfo();
- res.serviceInfo = PackageParser.generateServiceInfo(service,
- mFlags);
+ res.serviceInfo = PackageParser.generateServiceInfo(service, mFlags,
+ Binder.getOrigCallingUser());
if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
res.filter = filter;
}
@@ -4742,8 +4771,10 @@
intent.setPackage(targetPkg);
}
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ // TODO: Fix the userId argument
am.broadcastIntent(null, intent, null, finishedReceiver,
- 0, null, null, null, finishedReceiver != null, false);
+ 0, null, null, null, finishedReceiver != null, false,
+ Binder.getOrigCallingUser());
} catch (RemoteException ex) {
}
}
@@ -7584,7 +7615,7 @@
"Unknown component: " + packageName
+ "/" + className);
}
- if (!allowedByPermission && (uid != pkgSetting.userId)) {
+ if (!allowedByPermission && (!UserId.isSameApp(uid, pkgSetting.userId))) {
throw new SecurityException(
"Permission Denial: attempt to change component state from pid="
+ Binder.getCallingPid()
@@ -8673,4 +8704,8 @@
return mSettings.getVerifierDeviceIdentityLPw();
}
}
+
+ public List<UserInfo> getUsers() {
+ return mUserManager.getUsers();
+ }
}
diff --git a/services/java/com/android/server/pm/UserManager.java b/services/java/com/android/server/pm/UserManager.java
index 2687728..5eacf4a 100644
--- a/services/java/com/android/server/pm/UserManager.java
+++ b/services/java/com/android/server/pm/UserManager.java
@@ -24,6 +24,7 @@
import android.os.Environment;
import android.os.FileUtils;
import android.os.SystemClock;
+import android.os.UserId;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
@@ -169,9 +170,10 @@
* </user>
*/
private void writeUser(UserInfo userInfo) {
+ FileOutputStream fos = null;
try {
final File mUserFile = new File(mUsersDir, userInfo.id + ".xml");
- final FileOutputStream fos = new FileOutputStream(mUserFile);
+ fos = new FileOutputStream(mUserFile);
final BufferedOutputStream bos = new BufferedOutputStream(fos);
// XmlSerializer serializer = XmlUtils.serializerInstance();
@@ -193,6 +195,13 @@
serializer.endDocument();
} catch (IOException ioe) {
Slog.e(LOG_TAG, "Error writing user info " + userInfo.id + "\n" + ioe);
+ } finally {
+ if (fos != null) {
+ try {
+ fos.close();
+ } catch (IOException ioe) {
+ }
+ }
}
}
@@ -205,8 +214,9 @@
* </users>
*/
private void writeUserList() {
+ FileOutputStream fos = null;
try {
- final FileOutputStream fos = new FileOutputStream(mUserListFile);
+ fos = new FileOutputStream(mUserListFile);
final BufferedOutputStream bos = new BufferedOutputStream(fos);
// XmlSerializer serializer = XmlUtils.serializerInstance();
@@ -229,6 +239,13 @@
serializer.endDocument();
} catch (IOException ioe) {
Slog.e(LOG_TAG, "Error writing user list");
+ } finally {
+ if (fos != null) {
+ try {
+ fos.close();
+ } catch (IOException ioe) {
+ }
+ }
}
}
@@ -330,7 +347,7 @@
// Don't do it for the primary user, it will become recursive.
if (userId == 0)
continue;
- mInstaller.createUserData(packageName, PackageManager.getUid(userId, uid),
+ mInstaller.createUserData(packageName, UserId.getUid(userId, uid),
userId);
}
}
@@ -367,6 +384,8 @@
/**
* Returns the next available user id, filling in any holes in the ids.
+ * TODO: May not be a good idea to recycle ids, in case it results in confusion
+ * for data and battery stats collection, or unexpected cross-talk.
* @return
*/
private int getNextAvailableId() {
@@ -390,14 +409,8 @@
FileUtils.setPermissions(userPath.toString(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
| FileUtils.S_IXOTH, -1, -1);
- // Create the individual data directories
- for (ApplicationInfo app : apps) {
- if (app.uid > android.os.Process.FIRST_APPLICATION_UID
- && app.uid < PackageManager.PER_USER_RANGE) {
- mInstaller.createUserData(app.packageName,
- PackageManager.getUid(id, app.uid), id);
- }
- }
+ mInstaller.cloneUserData(0, id, false);
+
final long stopTime = SystemClock.elapsedRealtime();
Log.i(LOG_TAG,
"Time to create " + apps.size() + " packages = " + (stopTime - startTime) + "ms");
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 19d94a1..620d74c 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -95,6 +95,7 @@
import android.util.Slog;
import android.util.SparseIntArray;
import android.util.TypedValue;
+import android.view.Choreographer;
import android.view.Display;
import android.view.Gravity;
import android.view.IApplicationToken;
@@ -141,7 +142,8 @@
/** {@hide} */
public class WindowManagerService extends IWindowManager.Stub
- implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
+ implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs,
+ Choreographer.OnAnimateListener {
static final String TAG = "WindowManager";
static final boolean DEBUG = false;
static final boolean DEBUG_ADD_REMOVE = false;
@@ -456,7 +458,7 @@
int mDeferredRotationPauseCount;
boolean mLayoutNeeded = true;
- boolean mAnimationPending = false;
+ boolean mTraversalScheduled = false;
boolean mDisplayFrozen = false;
boolean mWaitingForConfig = false;
boolean mWindowsFreezingScreen = false;
@@ -503,7 +505,9 @@
final DisplayMetrics mTmpDisplayMetrics = new DisplayMetrics();
final DisplayMetrics mCompatDisplayMetrics = new DisplayMetrics();
- H mH = new H();
+ final H mH = new H();
+
+ final Choreographer mChoreographer = Choreographer.getInstance();
WindowState mCurrentFocus = null;
WindowState mLastFocus = null;
@@ -559,6 +563,7 @@
float mWindowAnimationScale = 1.0f;
float mTransitionAnimationScale = 1.0f;
+ float mAnimatorDurationScale = 1.0f;
final InputManager mInputManager;
@@ -691,6 +696,7 @@
Looper.prepare();
WindowManagerService s = new WindowManagerService(mContext, mPM,
mHaveInputMethods, mAllowBootMessages);
+ s.mChoreographer.addOnAnimateListener(s);
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_DISPLAY);
android.os.Process.setCanSelfBackground(false);
@@ -774,6 +780,8 @@
Settings.System.WINDOW_ANIMATION_SCALE, mWindowAnimationScale);
mTransitionAnimationScale = Settings.System.getFloat(context.getContentResolver(),
Settings.System.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScale);
+ mAnimatorDurationScale = Settings.System.getFloat(context.getContentResolver(),
+ Settings.System.ANIMATOR_DURATION_SCALE, mTransitionAnimationScale);
// Track changes to DevicePolicyManager state so we can enable/disable keyguard.
IntentFilter filter = new IntentFilter();
@@ -4657,6 +4665,7 @@
switch (which) {
case 0: mWindowAnimationScale = fixScale(scale); break;
case 1: mTransitionAnimationScale = fixScale(scale); break;
+ case 2: mAnimatorDurationScale = fixScale(scale); break;
}
// Persist setting
@@ -4676,6 +4685,9 @@
if (scales.length >= 2) {
mTransitionAnimationScale = fixScale(scales[1]);
}
+ if (scales.length >= 3) {
+ mAnimatorDurationScale = fixScale(scales[2]);
+ }
}
// Persist setting
@@ -4686,12 +4698,14 @@
switch (which) {
case 0: return mWindowAnimationScale;
case 1: return mTransitionAnimationScale;
+ case 2: return mAnimatorDurationScale;
}
return 0;
}
public float[] getAnimationScales() {
- return new float[] { mWindowAnimationScale, mTransitionAnimationScale };
+ return new float[] { mWindowAnimationScale, mTransitionAnimationScale,
+ mAnimatorDurationScale };
}
public int getSwitchState(int sw) {
@@ -5390,7 +5404,7 @@
if (mScreenRotationAnimation.setRotation(rotation, mFxSession,
MAX_ANIMATION_DURATION, mTransitionAnimationScale,
mCurDisplayWidth, mCurDisplayHeight)) {
- requestAnimationLocked(0);
+ mChoreographer.scheduleAnimation();
}
}
Surface.setOrientation(0, rotation);
@@ -6513,7 +6527,7 @@
final class H extends Handler {
public static final int REPORT_FOCUS_CHANGE = 2;
public static final int REPORT_LOSING_FOCUS = 3;
- public static final int ANIMATE = 4;
+ public static final int DO_TRAVERSAL = 4;
public static final int ADD_STARTING = 5;
public static final int REMOVE_STARTING = 6;
public static final int FINISHED_STARTING = 7;
@@ -6607,9 +6621,9 @@
}
} break;
- case ANIMATE: {
+ case DO_TRAVERSAL: {
synchronized(mWindowMap) {
- mAnimationPending = false;
+ mTraversalScheduled = false;
performLayoutAndPlaceSurfacesLocked();
}
} break;
@@ -6825,12 +6839,14 @@
Settings.System.WINDOW_ANIMATION_SCALE, mWindowAnimationScale);
Settings.System.putFloat(mContext.getContentResolver(),
Settings.System.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScale);
+ Settings.System.putFloat(mContext.getContentResolver(),
+ Settings.System.ANIMATOR_DURATION_SCALE, mAnimatorDurationScale);
break;
}
case FORCE_GC: {
synchronized(mWindowMap) {
- if (mAnimationPending) {
+ if (mChoreographer.isAnimationScheduled()) {
// If we are animating, don't do the gc now but
// delay a bit so we don't interrupt the animation.
mH.sendMessageDelayed(mH.obtainMessage(H.FORCE_GC),
@@ -7389,7 +7405,7 @@
} else {
mInLayout = false;
if (mLayoutNeeded) {
- requestAnimationLocked(0);
+ requestTraversalLocked();
}
}
if (mWindowsChanged && !mWindowChangeListeners.isEmpty()) {
@@ -8822,10 +8838,9 @@
needRelayout = adjustWallpaperWindowsLocked() != 0;
}
if (needRelayout) {
- requestAnimationLocked(0);
+ requestTraversalLocked();
} else if (animating) {
- final int refreshTimeUs = (int)(1000 / mDisplay.getRefreshRate());
- requestAnimationLocked(currentTime + refreshTimeUs - SystemClock.uptimeMillis());
+ mChoreographer.scheduleAnimation();
}
// Finally update all input windows now that the window changes have stabilized.
@@ -8944,10 +8959,17 @@
}
}
- void requestAnimationLocked(long delay) {
- if (!mAnimationPending) {
- mAnimationPending = true;
- mH.sendMessageDelayed(mH.obtainMessage(H.ANIMATE), delay);
+ void requestTraversalLocked() {
+ if (!mTraversalScheduled) {
+ mTraversalScheduled = true;
+ mH.sendEmptyMessage(H.DO_TRAVERSAL);
+ }
+ }
+
+ @Override
+ public void onAnimate() {
+ synchronized(mWindowMap) {
+ performLayoutAndPlaceSurfacesLocked();
}
}
@@ -9267,7 +9289,7 @@
if (DEBUG_ORIENTATION) Slog.i(TAG, "**** Dismissing screen rotation animation");
if (mScreenRotationAnimation.dismiss(mFxSession, MAX_ANIMATION_DURATION,
mTransitionAnimationScale, mCurDisplayWidth, mCurDisplayHeight)) {
- requestAnimationLocked(0);
+ mChoreographer.scheduleAnimation();
} else {
mScreenRotationAnimation = null;
updateRotation = true;
@@ -9759,9 +9781,10 @@
pw.print(" mLastWindowForcedOrientation"); pw.print(mLastWindowForcedOrientation);
pw.print(" mForcedAppOrientation="); pw.println(mForcedAppOrientation);
pw.print(" mDeferredRotationPauseCount="); pw.println(mDeferredRotationPauseCount);
- pw.print(" mAnimationPending="); pw.print(mAnimationPending);
+ pw.print(" mTraversalScheduled="); pw.print(mTraversalScheduled);
pw.print(" mWindowAnimationScale="); pw.print(mWindowAnimationScale);
pw.print(" mTransitionWindowAnimationScale="); pw.println(mTransitionAnimationScale);
+ pw.print(" mAnimatorDurationScale="); pw.println(mAnimatorDurationScale);
pw.print(" mNextAppTransition=0x");
pw.print(Integer.toHexString(mNextAppTransition));
pw.print(" mAppTransitionReady="); pw.println(mAppTransitionReady);
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index 1067cad..6868cf6 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -1593,7 +1593,7 @@
mService.applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_ENTER, true);
}
if (requestAnim) {
- mService.requestAnimationLocked(0);
+ mService.mChoreographer.scheduleAnimation();
}
return true;
}
@@ -1634,7 +1634,7 @@
}
}
if (requestAnim) {
- mService.requestAnimationLocked(0);
+ mService.mChoreographer.scheduleAnimation();
}
return true;
}
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index fc211e5..42e280f 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -32,7 +32,7 @@
endif
ifeq ($(TARGET_BOARD_PLATFORM), omap4)
LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY
- LOCAL_CFLAGS += -DUSE_TRIPLE_BUFFERING=1
+ LOCAL_CFLAGS += -DUSE_TRIPLE_BUFFERING
endif
ifeq ($(TARGET_BOARD_PLATFORM), s5pc110)
LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY -DNEVER_DEFAULT_TO_ASYNC_MODE
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
index f4afeea..69f1aca 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007 The Android Open Source Project
+ * 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.
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
@@ -22,15 +21,6 @@
#include <unistd.h>
#include <fcntl.h>
-#include <signal.h>
-#include <termios.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/resource.h>
-
-#include <linux/unistd.h>
#include <utils/Log.h>
@@ -45,39 +35,22 @@
// ----------------------------------------------------------------------------
-DisplayHardwareBase::DisplayEventThreadBase::DisplayEventThreadBase(
+DisplayHardwareBase::DisplayEventThread::DisplayEventThread(
const sp<SurfaceFlinger>& flinger)
: Thread(false), mFlinger(flinger) {
}
-DisplayHardwareBase::DisplayEventThreadBase::~DisplayEventThreadBase() {
+DisplayHardwareBase::DisplayEventThread::~DisplayEventThread() {
}
-// ----------------------------------------------------------------------------
-
-DisplayHardwareBase::DisplayEventThread::DisplayEventThread(
- const sp<SurfaceFlinger>& flinger)
- : DisplayEventThreadBase(flinger)
-{
+status_t DisplayHardwareBase::DisplayEventThread::initCheck() const {
+ return ((access(kSleepFileName, R_OK) == 0 &&
+ access(kWakeFileName, R_OK) == 0)) ? NO_ERROR : NO_INIT;
}
-DisplayHardwareBase::DisplayEventThread::~DisplayEventThread()
-{
-}
+bool DisplayHardwareBase::DisplayEventThread::threadLoop() {
-bool DisplayHardwareBase::DisplayEventThread::threadLoop()
-{
- int err = 0;
- char buf;
- int fd;
-
- fd = open(kSleepFileName, O_RDONLY, 0);
- do {
- err = read(fd, &buf, 1);
- } while (err < 0 && errno == EINTR);
- close(fd);
- ALOGW_IF(err<0, "ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno));
- if (err >= 0) {
+ if (waitForFbSleep() == NO_ERROR) {
sp<SurfaceFlinger> flinger = mFlinger.promote();
ALOGD("About to give-up screen, flinger = %p", flinger.get());
if (flinger != 0) {
@@ -85,39 +58,51 @@
flinger->screenReleased(0);
mBarrier.wait();
}
+ if (waitForFbWake() == NO_ERROR) {
+ sp<SurfaceFlinger> flinger = mFlinger.promote();
+ ALOGD("Screen about to return, flinger = %p", flinger.get());
+ if (flinger != 0) {
+ flinger->screenAcquired(0);
+ }
+ return true;
+ }
}
- fd = open(kWakeFileName, O_RDONLY, 0);
+
+ // error, exit the thread
+ return false;
+}
+
+status_t DisplayHardwareBase::DisplayEventThread::waitForFbSleep() {
+ int err = 0;
+ char buf;
+ int fd = open(kSleepFileName, O_RDONLY, 0);
+ // if the file doesn't exist, the error will be caught in read() below
do {
- err = read(fd, &buf, 1);
+ err = read(fd, &buf, 1);
} while (err < 0 && errno == EINTR);
close(fd);
- ALOGW_IF(err<0, "ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno));
- if (err >= 0) {
- sp<SurfaceFlinger> flinger = mFlinger.promote();
- ALOGD("Screen about to return, flinger = %p", flinger.get());
- if (flinger != 0)
- flinger->screenAcquired(0);
- }
- return true;
+ ALOGE_IF(err<0, "*** ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno));
+ return err < 0 ? -errno : int(NO_ERROR);
}
-status_t DisplayHardwareBase::DisplayEventThread::releaseScreen() const
-{
+status_t DisplayHardwareBase::DisplayEventThread::waitForFbWake() {
+ int err = 0;
+ char buf;
+ int fd = open(kWakeFileName, O_RDONLY, 0);
+ // if the file doesn't exist, the error will be caught in read() below
+ do {
+ err = read(fd, &buf, 1);
+ } while (err < 0 && errno == EINTR);
+ close(fd);
+ ALOGE_IF(err<0, "*** ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno));
+ return err < 0 ? -errno : int(NO_ERROR);
+}
+
+status_t DisplayHardwareBase::DisplayEventThread::releaseScreen() const {
mBarrier.open();
return NO_ERROR;
}
-status_t DisplayHardwareBase::DisplayEventThread::readyToRun()
-{
- return NO_ERROR;
-}
-
-status_t DisplayHardwareBase::DisplayEventThread::initCheck() const
-{
- return ((access(kSleepFileName, R_OK) == 0 &&
- access(kWakeFileName, R_OK) == 0)) ? NO_ERROR : NO_INIT;
-}
-
// ----------------------------------------------------------------------------
DisplayHardwareBase::DisplayHardwareBase(const sp<SurfaceFlinger>& flinger,
@@ -127,35 +112,35 @@
mDisplayEventThread = new DisplayEventThread(flinger);
}
-DisplayHardwareBase::~DisplayHardwareBase()
-{
+void DisplayHardwareBase::startSleepManagement() const {
+ if (mDisplayEventThread->initCheck() == NO_ERROR) {
+ mDisplayEventThread->run("DisplayEventThread", PRIORITY_URGENT_DISPLAY);
+ } else {
+ ALOGW("/sys/power/wait_for_fb_{wake|sleep} don't exist");
+ }
+}
+
+DisplayHardwareBase::~DisplayHardwareBase() {
// request exit
mDisplayEventThread->requestExitAndWait();
}
-bool DisplayHardwareBase::canDraw() const
-{
+bool DisplayHardwareBase::canDraw() const {
return mScreenAcquired;
}
-void DisplayHardwareBase::releaseScreen() const
-{
+void DisplayHardwareBase::releaseScreen() const {
status_t err = mDisplayEventThread->releaseScreen();
if (err >= 0) {
mScreenAcquired = false;
}
}
-void DisplayHardwareBase::acquireScreen() const
-{
- status_t err = mDisplayEventThread->acquireScreen();
- if (err >= 0) {
- mScreenAcquired = true;
- }
+void DisplayHardwareBase::acquireScreen() const {
+ mScreenAcquired = true;
}
-bool DisplayHardwareBase::isScreenAcquired() const
-{
+bool DisplayHardwareBase::isScreenAcquired() const {
return mScreenAcquired;
}
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
index ef2df43..fba211b 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007 The Android Open Source Project
+ * 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.
@@ -20,8 +20,6 @@
#include <stdint.h>
#include <utils/RefBase.h>
#include <utils/threads.h>
-#include <linux/kd.h>
-#include <linux/vt.h>
#include "Barrier.h"
namespace android {
@@ -31,11 +29,13 @@
class DisplayHardwareBase
{
public:
- DisplayHardwareBase(
- const sp<SurfaceFlinger>& flinger,
- uint32_t displayIndex);
+ DisplayHardwareBase(
+ const sp<SurfaceFlinger>& flinger,
+ uint32_t displayIndex);
- ~DisplayHardwareBase();
+ ~DisplayHardwareBase();
+
+ void startSleepManagement() const;
// console management
void releaseScreen() const;
@@ -46,34 +46,21 @@
private:
- class DisplayEventThreadBase : public Thread {
- protected:
+ class DisplayEventThread : public Thread {
wp<SurfaceFlinger> mFlinger;
- public:
- DisplayEventThreadBase(const sp<SurfaceFlinger>& flinger);
- virtual ~DisplayEventThreadBase();
- virtual void onFirstRef() {
- run("DisplayEventThread", PRIORITY_URGENT_DISPLAY);
- }
- virtual status_t acquireScreen() const { return NO_ERROR; };
- virtual status_t releaseScreen() const { return NO_ERROR; };
- virtual status_t initCheck() const = 0;
- };
-
- class DisplayEventThread : public DisplayEventThreadBase
- {
mutable Barrier mBarrier;
+ status_t waitForFbSleep();
+ status_t waitForFbWake();
public:
- DisplayEventThread(const sp<SurfaceFlinger>& flinger);
+ DisplayEventThread(const sp<SurfaceFlinger>& flinger);
virtual ~DisplayEventThread();
virtual bool threadLoop();
- virtual status_t readyToRun();
- virtual status_t releaseScreen() const;
- virtual status_t initCheck() const;
+ status_t releaseScreen() const;
+ status_t initCheck() const;
};
- sp<DisplayEventThreadBase> mDisplayEventThread;
- mutable int mScreenAcquired;
+ sp<DisplayEventThread> mDisplayEventThread;
+ mutable int mScreenAcquired;
};
}; // namespace android
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 9c04d59..3e6b872 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -55,6 +55,7 @@
mCurrentTransform(0),
mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
mCurrentOpacity(true),
+ mRefreshPending(0),
mFrameLatencyNeeded(false),
mFrameLatencyOffset(0),
mFormat(PIXEL_FORMAT_NONE),
@@ -113,7 +114,7 @@
void Layer::onFrameQueued() {
android_atomic_inc(&mQueuedFrames);
- mFlinger->signalEvent();
+ mFlinger->signalLayerUpdate();
}
// called with SurfaceFlinger::mStateLock as soon as the layer is entered
@@ -407,16 +408,37 @@
// pageflip handling...
// ----------------------------------------------------------------------------
+bool Layer::onPreComposition()
+{
+ // if there was more than one pending update, request a refresh
+ if (mRefreshPending >= 2) {
+ mRefreshPending = 0;
+ return true;
+ }
+ mRefreshPending = 0;
+ return false;
+}
+
void Layer::lockPageFlip(bool& recomputeVisibleRegions)
{
if (mQueuedFrames > 0) {
+
+ // if we've already called updateTexImage() without going through
+ // a composition step, we have to skip this layer at this point
+ // because we cannot call updateTeximage() without a corresponding
+ // compositionComplete() call.
+ // we'll trigger an update in onPreComposition().
+ if (mRefreshPending++) {
+ return;
+ }
+
// Capture the old state of the layer for comparisons later
const bool oldOpacity = isOpaque();
sp<GraphicBuffer> oldActiveBuffer = mActiveBuffer;
// signal another event if we have more frames pending
if (android_atomic_dec(&mQueuedFrames) > 1) {
- mFlinger->signalEvent();
+ mFlinger->signalLayerUpdate();
}
if (mSurfaceTexture->updateTexImage() < NO_ERROR) {
@@ -519,6 +541,10 @@
void Layer::unlockPageFlip(
const Transform& planeTransform, Region& outDirtyRegion)
{
+ if (mRefreshPending >= 2) {
+ return;
+ }
+
Region dirtyRegion(mPostedDirtyRegion);
if (!dirtyRegion.isEmpty()) {
mPostedDirtyRegion.clear();
@@ -552,9 +578,9 @@
snprintf(buffer, SIZE,
" "
"format=%2d, activeBuffer=[%4ux%4u:%4u,%3X],"
- " transform-hint=0x%02x, queued-frames=%d\n",
+ " transform-hint=0x%02x, queued-frames=%d, mRefreshPending=%d\n",
mFormat, w0, h0, s0,f0,
- getTransformHint(), mQueuedFrames);
+ getTransformHint(), mQueuedFrames, mRefreshPending);
result.append(buffer);
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 2dd4da1..bf30608 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -80,6 +80,7 @@
virtual wp<IBinder> getSurfaceTextureBinder() const;
virtual void onLayerDisplayed();
+ virtual bool onPreComposition();
// only for debugging
inline const sp<GraphicBuffer>& getActiveBuffer() const { return mActiveBuffer; }
@@ -115,14 +116,17 @@
uint32_t mCurrentTransform;
uint32_t mCurrentScalingMode;
bool mCurrentOpacity;
+ size_t mRefreshPending;
bool mFrameLatencyNeeded;
int mFrameLatencyOffset;
+
struct Statistics {
Statistics() : timestamp(0), set(0), vsync(0) { }
nsecs_t timestamp; // buffer timestamp
nsecs_t set; // buffer displayed timestamp
nsecs_t vsync; // vsync immediately before set
};
+
// protected by mLock
Statistics mFrameStats[128];
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index d32fcdd..e764001 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -47,8 +47,7 @@
mOrientation(0),
mPlaneOrientation(0),
mTransactionFlags(0),
- mPremultipliedAlpha(true), mName("unnamed"), mDebug(false),
- mInvalidate(0)
+ mPremultipliedAlpha(true), mName("unnamed"), mDebug(false)
{
const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware());
mFlags = hw.getFlags();
@@ -262,23 +261,11 @@
mTransformedBounds = tr.makeBounds(w, h);
}
-void LayerBase::lockPageFlip(bool& recomputeVisibleRegions)
-{
+void LayerBase::lockPageFlip(bool& recomputeVisibleRegions) {
}
void LayerBase::unlockPageFlip(
- const Transform& planeTransform, Region& outDirtyRegion)
-{
- if ((android_atomic_and(~1, &mInvalidate)&1) == 1) {
- outDirtyRegion.orSelf(visibleRegionScreen);
- }
-}
-
-void LayerBase::invalidate()
-{
- if ((android_atomic_or(1, &mInvalidate)&1) == 0) {
- mFlinger->signalEvent();
- }
+ const Transform& planeTransform, Region& outDirtyRegion) {
}
void LayerBase::drawRegion(const Region& reg) const
@@ -471,16 +458,21 @@
void LayerBase::dump(String8& result, char* buffer, size_t SIZE) const
{
const Layer::State& s(drawingState());
+
+ snprintf(buffer, SIZE,
+ "+ %s %p (%s)\n",
+ getTypeId(), this, getName().string());
+ result.append(buffer);
+
s.transparentRegion.dump(result, "transparentRegion");
transparentRegionScreen.dump(result, "transparentRegionScreen");
visibleRegionScreen.dump(result, "visibleRegionScreen");
+
snprintf(buffer, SIZE,
- "+ %s %p (%s)\n"
" "
"z=%9d, pos=(%g,%g), size=(%4d,%4d), "
"isOpaque=%1d, needsDithering=%1d, invalidate=%1d, "
"alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n",
- getTypeId(), this, getName().string(),
s.z, s.transform.tx(), s.transform.ty(), s.w, s.h,
isOpaque(), needsDithering(), contentDirty,
s.alpha, s.flags,
diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h
index 6b62c9d..b8f7680 100644
--- a/services/surfaceflinger/LayerBase.h
+++ b/services/surfaceflinger/LayerBase.h
@@ -103,8 +103,6 @@
Rect visibleBounds() const;
void drawRegion(const Region& reg) const;
- void invalidate();
-
virtual sp<LayerBaseClient> getLayerBaseClient() const { return 0; }
virtual sp<Layer> getLayer() const { return 0; }
@@ -204,9 +202,16 @@
/** called with the state lock when the surface is removed from the
* current list */
- virtual void onRemoved() { };
+ virtual void onRemoved() { }
- virtual void onLayerDisplayed() { };
+ /** called after page-flip
+ */
+ virtual void onLayerDisplayed() { }
+
+ /** called before composition.
+ * returns true if the layer has pending updates.
+ */
+ virtual bool onPreComposition() { return false; }
/** always call base class first */
virtual void dump(String8& result, char* scratch, size_t size) const;
@@ -275,10 +280,6 @@
mutable bool mDebug;
- // atomic
- volatile int32_t mInvalidate;
-
-
public:
// called from class SurfaceFlinger
virtual ~LayerBase();
diff --git a/services/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/MessageQueue.cpp
index 70711e7..290fff4 100644
--- a/services/surfaceflinger/MessageQueue.cpp
+++ b/services/surfaceflinger/MessageQueue.cpp
@@ -29,6 +29,7 @@
#include "MessageQueue.h"
#include "EventThread.h"
+#include "SurfaceFlinger.h"
namespace android {
@@ -48,14 +49,47 @@
// ---------------------------------------------------------------------------
+void MessageQueue::Handler::signalRefresh() {
+ if ((android_atomic_or(eventMaskRefresh, &mEventMask) & eventMaskRefresh) == 0) {
+ mQueue.mLooper->sendMessage(this, Message(MessageQueue::REFRESH));
+ }
+}
+
+void MessageQueue::Handler::signalInvalidate() {
+ if ((android_atomic_or(eventMaskInvalidate, &mEventMask) & eventMaskInvalidate) == 0) {
+ mQueue.mLooper->sendMessage(this, Message(MessageQueue::INVALIDATE));
+ }
+}
+
+void MessageQueue::Handler::handleMessage(const Message& message) {
+ switch (message.what) {
+ case INVALIDATE:
+ android_atomic_and(~eventMaskInvalidate, &mEventMask);
+ mQueue.mFlinger->onMessageReceived(message.what);
+ break;
+ case REFRESH:
+ android_atomic_and(~eventMaskRefresh, &mEventMask);
+ mQueue.mFlinger->onMessageReceived(message.what);
+ break;
+ }
+}
+
+// ---------------------------------------------------------------------------
+
MessageQueue::MessageQueue()
- : mLooper(new Looper(true)), mWorkPending(0)
{
}
MessageQueue::~MessageQueue() {
}
+void MessageQueue::init(const sp<SurfaceFlinger>& flinger)
+{
+ mFlinger = flinger;
+ mLooper = new Looper(true);
+ mHandler = new Handler(*this);
+}
+
void MessageQueue::setEventThread(const sp<EventThread>& eventThread)
{
mEventThread = eventThread;
@@ -68,25 +102,16 @@
void MessageQueue::waitMessage() {
do {
IPCThreadState::self()->flushCommands();
-
int32_t ret = mLooper->pollOnce(-1);
switch (ret) {
case ALOOPER_POLL_WAKE:
case ALOOPER_POLL_CALLBACK:
- // callback and/or wake
- if (android_atomic_and(0, &mWorkPending)) {
- return;
- }
continue;
-
+ case ALOOPER_POLL_ERROR:
+ ALOGE("ALOOPER_POLL_ERROR");
case ALOOPER_POLL_TIMEOUT:
// timeout (should not happen)
continue;
-
- case ALOOPER_POLL_ERROR:
- ALOGE("ALOOPER_POLL_ERROR");
- continue;
-
default:
// should not happen
ALOGE("Looper::pollOnce() returned unknown status %d", ret);
@@ -107,15 +132,13 @@
return NO_ERROR;
}
-void MessageQueue::scheduleWorkASAP() {
- if (android_atomic_or(1, &mWorkPending) == 0) {
- mLooper->wake();
- }
+void MessageQueue::invalidate() {
+// mHandler->signalInvalidate();
+ mEvents->requestNextVsync();
}
-status_t MessageQueue::invalidate() {
+void MessageQueue::refresh() {
mEvents->requestNextVsync();
- return NO_ERROR;
}
int MessageQueue::cb_eventReceiver(int fd, int events, void* data) {
@@ -126,10 +149,10 @@
int MessageQueue::eventReceiver(int fd, int events) {
ssize_t n;
DisplayEventReceiver::Event buffer[8];
- while ((n = getEvents(buffer, 8)) > 0) {
+ while ((n = DisplayEventReceiver::getEvents(mEventTube, buffer, 8)) > 0) {
for (int i=0 ; i<n ; i++) {
if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
- scheduleWorkASAP();
+ mHandler->signalRefresh();
break;
}
}
@@ -137,24 +160,6 @@
return 1;
}
-ssize_t MessageQueue::getEvents(
- DisplayEventReceiver::Event* events, size_t count)
-{
- ssize_t size = mEventTube->read(events, sizeof(events[0])*count);
- ALOGE_IF(size<0, "MessageQueue::getEvents error (%s)", strerror(-size));
- if (size >= 0) {
- // Note: if (size % sizeof(events[0])) != 0, we've got a
- // partial read. This can happen if the queue filed up (ie: if we
- // didn't pull from it fast enough).
- // We discard the partial event and rely on the sender to
- // re-send the event if appropriate (some events, like VSYNC
- // can be lost forever).
- // returns number of events read
- size /= sizeof(events[0]);
- }
- return size;
-}
-
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/services/surfaceflinger/MessageQueue.h b/services/surfaceflinger/MessageQueue.h
index 5ea197d..ea29e7e 100644
--- a/services/surfaceflinger/MessageQueue.h
+++ b/services/surfaceflinger/MessageQueue.h
@@ -33,6 +33,7 @@
class IDisplayEventConnection;
class EventThread;
+class SurfaceFlinger;
// ---------------------------------------------------------------------------
@@ -59,25 +60,48 @@
// ---------------------------------------------------------------------------
class MessageQueue {
+ class Handler : public MessageHandler {
+ enum {
+ eventMaskInvalidate = 0x1,
+ eventMaskRefresh = 0x2
+ };
+ MessageQueue& mQueue;
+ int32_t mEventMask;
+ public:
+ Handler(MessageQueue& queue) : mQueue(queue), mEventMask(0) { }
+ virtual void handleMessage(const Message& message);
+ void signalRefresh();
+ void signalInvalidate();
+ };
+
+ friend class Handler;
+
+ sp<SurfaceFlinger> mFlinger;
sp<Looper> mLooper;
sp<EventThread> mEventThread;
sp<IDisplayEventConnection> mEvents;
sp<BitTube> mEventTube;
- int32_t mWorkPending;
+ sp<Handler> mHandler;
+
static int cb_eventReceiver(int fd, int events, void* data);
int eventReceiver(int fd, int events);
- ssize_t getEvents(DisplayEventReceiver::Event* events, size_t count);
- void scheduleWorkASAP();
public:
+ enum {
+ INVALIDATE = 0,
+ REFRESH = 1,
+ };
+
MessageQueue();
~MessageQueue();
+ void init(const sp<SurfaceFlinger>& flinger);
void setEventThread(const sp<EventThread>& events);
void waitMessage();
status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime=0);
- status_t invalidate();
+ void invalidate();
+ void refresh();
};
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index ff70ec3..ab09bfa 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -125,11 +125,34 @@
ALOGI_IF(mDebugDDMS, "DDMS debugging enabled");
}
+void SurfaceFlinger::onFirstRef()
+{
+ mEventQueue.init(this);
+
+ run("SurfaceFlinger", PRIORITY_URGENT_DISPLAY);
+
+ // Wait for the main thread to be done with its initialization
+ mReadyToRunBarrier.wait();
+}
+
+
SurfaceFlinger::~SurfaceFlinger()
{
glDeleteTextures(1, &mWormholeTexName);
}
+void SurfaceFlinger::binderDied(const wp<IBinder>& who)
+{
+ // the window manager died on us. prepare its eulogy.
+
+ // reset screen orientation
+ Vector<ComposerState> state;
+ setTransactionState(state, eOrientationDefault, 0);
+
+ // restart the boot-animation
+ property_set("ctl.start", "bootanim");
+}
+
sp<IMemoryHeap> SurfaceFlinger::getCblk() const
{
return mServerHeap;
@@ -183,26 +206,6 @@
property_set("ctl.stop", "bootanim");
}
-void SurfaceFlinger::binderDied(const wp<IBinder>& who)
-{
- // the window manager died on us. prepare its eulogy.
-
- // reset screen orientation
- Vector<ComposerState> state;
- setTransactionState(state, eOrientationDefault, 0);
-
- // restart the boot-animation
- property_set("ctl.start", "bootanim");
-}
-
-void SurfaceFlinger::onFirstRef()
-{
- run("SurfaceFlinger", PRIORITY_URGENT_DISPLAY);
-
- // Wait for the main thread to be done with its initialization
- mReadyToRunBarrier.wait();
-}
-
static inline uint16_t pack565(int r, int g, int b) {
return (r<<11)|(g<<5)|b;
}
@@ -297,6 +300,7 @@
// start the EventThread
mEventThread = new EventThread(this);
mEventQueue.setEventThread(mEventThread);
+ hw.startSleepManagement();
/*
* We're now ready to accept clients...
@@ -311,34 +315,6 @@
}
// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark Events Handler
-#endif
-
-void SurfaceFlinger::waitForEvent() {
- mEventQueue.waitMessage();
-}
-
-void SurfaceFlinger::signalEvent() {
- mEventQueue.invalidate();
-}
-
-status_t SurfaceFlinger::postMessageAsync(const sp<MessageBase>& msg,
- nsecs_t reltime, uint32_t flags) {
- return mEventQueue.postMessage(msg, reltime);
-}
-
-status_t SurfaceFlinger::postMessageSync(const sp<MessageBase>& msg,
- nsecs_t reltime, uint32_t flags) {
- status_t res = mEventQueue.postMessage(msg, reltime);
- if (res == NO_ERROR) {
- msg->wait();
- }
- return res;
-}
-
-// ----------------------------------------------------------------------------
bool SurfaceFlinger::authenticateSurfaceTexture(
const sp<ISurfaceTexture>& surfaceTexture) const {
@@ -388,59 +364,104 @@
}
// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark Main loop
-#endif
+
+void SurfaceFlinger::waitForEvent() {
+ mEventQueue.waitMessage();
+}
+
+void SurfaceFlinger::signalTransaction() {
+ mEventQueue.invalidate();
+}
+
+void SurfaceFlinger::signalLayerUpdate() {
+ mEventQueue.invalidate();
+}
+
+void SurfaceFlinger::signalRefresh() {
+ mEventQueue.refresh();
+}
+
+status_t SurfaceFlinger::postMessageAsync(const sp<MessageBase>& msg,
+ nsecs_t reltime, uint32_t flags) {
+ return mEventQueue.postMessage(msg, reltime);
+}
+
+status_t SurfaceFlinger::postMessageSync(const sp<MessageBase>& msg,
+ nsecs_t reltime, uint32_t flags) {
+ status_t res = mEventQueue.postMessage(msg, reltime);
+ if (res == NO_ERROR) {
+ msg->wait();
+ }
+ return res;
+}
bool SurfaceFlinger::threadLoop()
{
waitForEvent();
-
- // check for transactions
- if (CC_UNLIKELY(mConsoleSignals)) {
- handleConsoleEvents();
- }
-
- // if we're in a global transaction, don't do anything.
- const uint32_t mask = eTransactionNeeded | eTraversalNeeded;
- uint32_t transactionFlags = peekTransactionFlags(mask);
- if (CC_UNLIKELY(transactionFlags)) {
- handleTransaction(transactionFlags);
- }
-
- // post surfaces (if needed)
- handlePageFlip();
-
- if (mDirtyRegion.isEmpty()) {
- // nothing new to do.
- return true;
- }
-
- if (CC_UNLIKELY(mHwWorkListDirty)) {
- // build the h/w work list
- handleWorkList();
- }
-
- const DisplayHardware& hw(graphicPlane(0).displayHardware());
- if (CC_LIKELY(hw.canDraw())) {
- // repaint the framebuffer (if needed)
- handleRepaint();
- // inform the h/w that we're done compositing
- hw.compositionComplete();
- postFramebuffer();
- } else {
- // pretend we did the post
- hw.compositionComplete();
- }
return true;
}
+void SurfaceFlinger::onMessageReceived(int32_t what)
+{
+ switch (what) {
+ case MessageQueue::REFRESH: {
+// case MessageQueue::INVALIDATE: {
+ // check for transactions
+ if (CC_UNLIKELY(mConsoleSignals)) {
+ handleConsoleEvents();
+ }
+
+ // if we're in a global transaction, don't do anything.
+ const uint32_t mask = eTransactionNeeded | eTraversalNeeded;
+ uint32_t transactionFlags = peekTransactionFlags(mask);
+ if (CC_UNLIKELY(transactionFlags)) {
+ handleTransaction(transactionFlags);
+ }
+
+ // post surfaces (if needed)
+ handlePageFlip();
+
+// signalRefresh();
+//
+// } break;
+//
+// case MessageQueue::REFRESH: {
+
+ handleRefresh();
+
+ const DisplayHardware& hw(graphicPlane(0).displayHardware());
+
+// if (mDirtyRegion.isEmpty()) {
+// return;
+// }
+
+ if (CC_UNLIKELY(mHwWorkListDirty)) {
+ // build the h/w work list
+ handleWorkList();
+ }
+
+ if (CC_LIKELY(hw.canDraw())) {
+ // repaint the framebuffer (if needed)
+ handleRepaint();
+ // inform the h/w that we're done compositing
+ hw.compositionComplete();
+ postFramebuffer();
+ } else {
+ // pretend we did the post
+ hw.compositionComplete();
+ }
+
+ } break;
+ }
+}
+
void SurfaceFlinger::postFramebuffer()
{
- // this should never happen. we do the flip anyways so we don't
- // risk to cause a deadlock with hwc
- ALOGW_IF(mSwapRegion.isEmpty(), "mSwapRegion is empty");
+ // mSwapRegion can be empty here is some cases, for instance if a hidden
+ // or fully transparent window is updating.
+ // in that case, we need to flip anyways to not risk a deadlock with
+ // h/w composer.
+
const DisplayHardware& hw(graphicPlane(0).displayHardware());
const nsecs_t now = systemTime();
mDebugInSwapBuffers = now;
@@ -717,13 +738,13 @@
void SurfaceFlinger::handlePageFlip()
{
- bool visibleRegions = mVisibleRegionsDirty;
- const LayerVector& currentLayers(mDrawingState.layersSortedByZ);
- visibleRegions |= lockPageFlip(currentLayers);
+ const DisplayHardware& hw = graphicPlane(0).displayHardware();
+ const Region screenRegion(hw.bounds());
- const DisplayHardware& hw = graphicPlane(0).displayHardware();
- const Region screenRegion(hw.bounds());
- if (visibleRegions) {
+ const LayerVector& currentLayers(mDrawingState.layersSortedByZ);
+ const bool visibleRegions = lockPageFlip(currentLayers);
+
+ if (visibleRegions || mVisibleRegionsDirty) {
Region opaqueRegion;
computeVisibleRegions(currentLayers, mDirtyRegion, opaqueRegion);
@@ -770,7 +791,7 @@
{
const GraphicPlane& plane(graphicPlane(0));
const Transform& planeTransform(plane.transform());
- size_t count = currentLayers.size();
+ const size_t count = currentLayers.size();
sp<LayerBase> const* layers = currentLayers.array();
for (size_t i=0 ; i<count ; i++) {
const sp<LayerBase>& layer(layers[i]);
@@ -778,6 +799,23 @@
}
}
+void SurfaceFlinger::handleRefresh()
+{
+ bool needInvalidate = false;
+ const LayerVector& currentLayers(mDrawingState.layersSortedByZ);
+ const size_t count = currentLayers.size();
+ for (size_t i=0 ; i<count ; i++) {
+ const sp<LayerBase>& layer(currentLayers[i]);
+ if (layer->onPreComposition()) {
+ needInvalidate = true;
+ }
+ }
+ if (needInvalidate) {
+ signalLayerUpdate();
+ }
+}
+
+
void SurfaceFlinger::handleWorkList()
{
mHwWorkListDirty = false;
@@ -1175,7 +1213,7 @@
{
uint32_t old = android_atomic_or(flags, &mTransactionFlags);
if ((old & flags)==0) { // wake the server up
- signalEvent();
+ signalTransaction();
}
return old;
}
@@ -1426,14 +1464,14 @@
{
// this may be called by a signal handler, we can't do too much in here
android_atomic_or(eConsoleReleased, &mConsoleSignals);
- signalEvent();
+ signalTransaction();
}
void SurfaceFlinger::screenAcquired(int dpy)
{
// this may be called by a signal handler, we can't do too much in here
android_atomic_or(eConsoleAcquired, &mConsoleSignals);
- signalEvent();
+ signalTransaction();
}
status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args)
@@ -1620,11 +1658,13 @@
snprintf(buffer, SIZE,
" last eglSwapBuffers() time: %f us\n"
" last transaction time : %f us\n"
+ " transaction-flags : %08x\n"
" refresh-rate : %f fps\n"
" x-dpi : %f\n"
" y-dpi : %f\n",
mLastSwapBufferTime/1000.0,
mLastTransactionTime/1000.0,
+ mTransactionFlags,
hw.getRefreshRate(),
hw.getDpiX(),
hw.getDpiY());
@@ -1769,7 +1809,7 @@
const DisplayHardware& hw(graphicPlane(0).displayHardware());
const Rect bounds(hw.getBounds());
setInvalidateRegion(Region(bounds));
- signalEvent();
+ signalTransaction();
}
void SurfaceFlinger::setInvalidateRegion(const Region& reg) {
@@ -2245,7 +2285,7 @@
// make sure to redraw the whole screen when the animation is done
mDirtyRegion.set(hw.bounds());
- signalEvent();
+ signalTransaction();
return NO_ERROR;
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index c24a9de..fcd9361 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -190,6 +190,8 @@
status_t renderScreenToTextureLocked(DisplayID dpy,
GLuint* textureName, GLfloat* uOut, GLfloat* vOut);
+ void onMessageReceived(int32_t what);
+
status_t postMessageAsync(const sp<MessageBase>& msg,
nsecs_t reltime=0, uint32_t flags = 0);
@@ -283,7 +285,10 @@
public: // hack to work around gcc 4.0.3 bug
const GraphicPlane& graphicPlane(int dpy) const;
GraphicPlane& graphicPlane(int dpy);
- void signalEvent();
+
+ void signalTransaction();
+ void signalLayerUpdate();
+ void signalRefresh();
void repaintEverything();
private:
@@ -300,6 +305,7 @@
void handlePageFlip();
bool lockPageFlip(const LayerVector& currentLayers);
void unlockPageFlip(const LayerVector& currentLayers);
+ void handleRefresh();
void handleWorkList();
void handleRepaint();
void postFramebuffer();
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 36a2567..e863f8b 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -21,7 +21,6 @@
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.NetworkPolicy.LIMIT_DISABLED;
-import static android.net.NetworkPolicy.SNOOZE_NEVER;
import static android.net.NetworkPolicy.WARNING_DISABLED;
import static android.net.NetworkPolicyManager.POLICY_NONE;
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
@@ -29,6 +28,8 @@
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
import static android.net.NetworkPolicyManager.computeNextCycleBoundary;
+import static android.net.TrafficStats.KB_IN_BYTES;
+import static android.net.TrafficStats.MB_IN_BYTES;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT;
@@ -101,10 +102,6 @@
private static final long TEST_START = 1194220800000L;
private static final String TEST_IFACE = "test0";
- private static final long KB_IN_BYTES = 1024;
- private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
- private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
-
private static NetworkTemplate sTemplateWifi = NetworkTemplate.buildTemplateWifi();
private BroadcastInterceptingContext mServiceContext;
@@ -256,41 +253,49 @@
}
public void testPidForegroundCombined() throws Exception {
+ IdleFuture idle;
+
// push all uid into background
+ idle = expectIdle();
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false);
mProcessObserver.onForegroundActivitiesChanged(PID_3, UID_B, false);
- waitUntilIdle();
+ idle.get();
assertFalse(mService.isUidForeground(UID_A));
assertFalse(mService.isUidForeground(UID_B));
// push one of the shared pids into foreground
+ idle = expectIdle();
mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, true);
- waitUntilIdle();
+ idle.get();
assertTrue(mService.isUidForeground(UID_A));
assertFalse(mService.isUidForeground(UID_B));
// and swap another uid into foreground
+ idle = expectIdle();
mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false);
mProcessObserver.onForegroundActivitiesChanged(PID_3, UID_B, true);
- waitUntilIdle();
+ idle.get();
assertFalse(mService.isUidForeground(UID_A));
assertTrue(mService.isUidForeground(UID_B));
// push both pid into foreground
+ idle = expectIdle();
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, true);
- waitUntilIdle();
+ idle.get();
assertTrue(mService.isUidForeground(UID_A));
// pull one out, should still be foreground
+ idle = expectIdle();
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
- waitUntilIdle();
+ idle.get();
assertTrue(mService.isUidForeground(UID_A));
// pull final pid out, should now be background
+ idle = expectIdle();
mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false);
- waitUntilIdle();
+ idle.get();
assertFalse(mService.isUidForeground(UID_A));
}
@@ -434,7 +439,7 @@
final long expectedCycle = parseTime("2007-11-05T00:00:00.000Z");
final NetworkPolicy policy = new NetworkPolicy(
- sTemplateWifi, 5, 1024L, 1024L, SNOOZE_NEVER, false);
+ sTemplateWifi, 5, 1024L, 1024L, false);
final long actualCycle = computeLastCycleBoundary(currentTime, policy);
assertTimeEquals(expectedCycle, actualCycle);
}
@@ -445,7 +450,7 @@
final long expectedCycle = parseTime("2007-10-20T00:00:00.000Z");
final NetworkPolicy policy = new NetworkPolicy(
- sTemplateWifi, 20, 1024L, 1024L, SNOOZE_NEVER, false);
+ sTemplateWifi, 20, 1024L, 1024L, false);
final long actualCycle = computeLastCycleBoundary(currentTime, policy);
assertTimeEquals(expectedCycle, actualCycle);
}
@@ -456,7 +461,7 @@
final long expectedCycle = parseTime("2007-01-30T00:00:00.000Z");
final NetworkPolicy policy = new NetworkPolicy(
- sTemplateWifi, 30, 1024L, 1024L, SNOOZE_NEVER, false);
+ sTemplateWifi, 30, 1024L, 1024L, false);
final long actualCycle = computeLastCycleBoundary(currentTime, policy);
assertTimeEquals(expectedCycle, actualCycle);
}
@@ -467,14 +472,14 @@
final long expectedCycle = parseTime("2007-02-28T23:59:59.000Z");
final NetworkPolicy policy = new NetworkPolicy(
- sTemplateWifi, 30, 1024L, 1024L, SNOOZE_NEVER, false);
+ sTemplateWifi, 30, 1024L, 1024L, false);
final long actualCycle = computeLastCycleBoundary(currentTime, policy);
assertTimeEquals(expectedCycle, actualCycle);
}
public void testNextCycleSane() throws Exception {
final NetworkPolicy policy = new NetworkPolicy(
- sTemplateWifi, 31, WARNING_DISABLED, LIMIT_DISABLED, SNOOZE_NEVER, false);
+ sTemplateWifi, 31, WARNING_DISABLED, LIMIT_DISABLED, false);
final LinkedHashSet<Long> seen = new LinkedHashSet<Long>();
// walk forwards, ensuring that cycle boundaries don't get stuck
@@ -489,7 +494,7 @@
public void testLastCycleSane() throws Exception {
final NetworkPolicy policy = new NetworkPolicy(
- sTemplateWifi, 31, WARNING_DISABLED, LIMIT_DISABLED, SNOOZE_NEVER, false);
+ sTemplateWifi, 31, WARNING_DISABLED, LIMIT_DISABLED, false);
final LinkedHashSet<Long> seen = new LinkedHashSet<Long>();
// walk backwards, ensuring that cycle boundaries look sane
@@ -547,7 +552,7 @@
replay();
setNetworkPolicies(new NetworkPolicy(
- sTemplateWifi, CYCLE_DAY, 1 * MB_IN_BYTES, 2 * MB_IN_BYTES, SNOOZE_NEVER, false));
+ sTemplateWifi, CYCLE_DAY, 1 * MB_IN_BYTES, 2 * MB_IN_BYTES, false));
future.get();
verifyAndReset();
}
@@ -604,9 +609,8 @@
future = expectMeteredIfacesChanged();
replay();
- setNetworkPolicies(
- new NetworkPolicy(sTemplateWifi, CYCLE_DAY, 1 * MB_IN_BYTES, 2 * MB_IN_BYTES,
- SNOOZE_NEVER, false));
+ setNetworkPolicies(new NetworkPolicy(sTemplateWifi, CYCLE_DAY, 1 * MB_IN_BYTES,
+ 2 * MB_IN_BYTES, false));
future.get();
verifyAndReset();
}
@@ -698,7 +702,7 @@
tagFuture = expectEnqueueNotification();
replay();
- mService.snoozePolicy(sTemplateWifi);
+ mService.snoozeLimit(sTemplateWifi);
assertNotificationType(TYPE_LIMIT_SNOOZED, tagFuture.get());
future.get();
verifyAndReset();
@@ -736,9 +740,8 @@
future = expectMeteredIfacesChanged(TEST_IFACE);
replay();
- setNetworkPolicies(
- new NetworkPolicy(sTemplateWifi, CYCLE_DAY, WARNING_DISABLED, LIMIT_DISABLED,
- SNOOZE_NEVER, true));
+ setNetworkPolicies(new NetworkPolicy(sTemplateWifi, CYCLE_DAY, WARNING_DISABLED,
+ LIMIT_DISABLED, true));
future.get();
verifyAndReset();
}
@@ -890,10 +893,10 @@
/**
* Wait until {@link #mService} internal {@link Handler} is idle.
*/
- private void waitUntilIdle() throws Exception {
+ private IdleFuture expectIdle() {
final IdleFuture future = new IdleFuture();
mService.addIdleHandler(future);
- future.get();
+ return future;
}
private static void assertTimeEquals(long expected, long actual) {
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index 8f5e77e..daf2018 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -31,6 +31,7 @@
import static android.net.NetworkStatsHistory.FIELD_ALL;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.NetworkTemplate.buildTemplateWifi;
+import static android.net.TrafficStats.MB_IN_BYTES;
import static android.net.TrafficStats.UID_REMOVED;
import static android.net.TrafficStats.UID_TETHERING;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
@@ -92,10 +93,6 @@
private static final String IMSI_1 = "310004";
private static final String IMSI_2 = "310260";
- private static final long KB_IN_BYTES = 1024;
- private static final long MB_IN_BYTES = 1024 * KB_IN_BYTES;
- private static final long GB_IN_BYTES = 1024 * MB_IN_BYTES;
-
private static NetworkTemplate sTemplateWifi = buildTemplateWifi();
private static NetworkTemplate sTemplateImsi1 = buildTemplateMobileAll(IMSI_1);
private static NetworkTemplate sTemplateImsi2 = buildTemplateMobileAll(IMSI_2);
diff --git a/telephony/java/com/android/internal/telephony/CommandsInterface.java b/telephony/java/com/android/internal/telephony/CommandsInterface.java
index 7900a9d..a0efab2 100644
--- a/telephony/java/com/android/internal/telephony/CommandsInterface.java
+++ b/telephony/java/com/android/internal/telephony/CommandsInterface.java
@@ -759,6 +759,15 @@
* retMsg.obj = AsyncResult ar
* ar.exception carries exception on failure
* ar.userObject contains the orignal value of result.obj
+ * ar.result is String containing IMSI on success
+ */
+ void getIMSIForApp(String aid, Message result);
+
+ /**
+ * returned message
+ * retMsg.obj = AsyncResult ar
+ * ar.exception carries exception on failure
+ * ar.userObject contains the orignal value of result.obj
* ar.result is String containing IMEI on success
*/
void getIMEI(Message result);
@@ -1050,6 +1059,14 @@
String data, String pin2, Message response);
/**
+ * parameters equivalent to 27.007 AT+CRSM command
+ * response.obj will be an AsyncResult
+ * response.obj.userObj will be a IccIoResult on success
+ */
+ void iccIOForApp (int command, int fileid, String path, int p1, int p2, int p3,
+ String data, String pin2, String aid, Message response);
+
+ /**
* (AsyncResult)response.obj).result is an int[] with element [0] set to
* 1 for "CLIP is provisioned", and 0 for "CLIP is not provisioned".
*
diff --git a/telephony/java/com/android/internal/telephony/IccCardStatus.java b/telephony/java/com/android/internal/telephony/IccCardStatus.java
index c751a21..a3bdd76 100644
--- a/telephony/java/com/android/internal/telephony/IccCardStatus.java
+++ b/telephony/java/com/android/internal/telephony/IccCardStatus.java
@@ -24,7 +24,7 @@
* {@hide}
*/
public class IccCardStatus {
- static final int CARD_MAX_APPS = 8;
+ public static final int CARD_MAX_APPS = 8;
public enum CardState {
CARDSTATE_ABSENT,
diff --git a/telephony/java/com/android/internal/telephony/IccFileHandler.java b/telephony/java/com/android/internal/telephony/IccFileHandler.java
index 93b9b79..380bfd1 100644
--- a/telephony/java/com/android/internal/telephony/IccFileHandler.java
+++ b/telephony/java/com/android/internal/telephony/IccFileHandler.java
@@ -145,8 +145,9 @@
= obtainMessage(EVENT_GET_RECORD_SIZE_DONE,
new LoadLinearFixedContext(fileid, recordNum, onLoaded));
- phone.mCM.iccIO(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),
- 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, response);
+ phone.mCM.iccIOForApp(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),
+ 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null,
+ phone.getIccCard().getAid(), response);
}
/**
@@ -164,9 +165,10 @@
onLoaded));
// TODO(): Verify when path changes are done.
- phone.mCM.iccIO(COMMAND_GET_RESPONSE, IccConstants.EF_IMG, "img",
+ phone.mCM.iccIOForApp(COMMAND_GET_RESPONSE, IccConstants.EF_IMG, "img",
recordNum, READ_RECORD_MODE_ABSOLUTE,
- GET_RESPONSE_EF_IMG_SIZE_BYTES, null, null, response);
+ GET_RESPONSE_EF_IMG_SIZE_BYTES, null, null,
+ phone.getIccCard().getAid(), response);
}
/**
@@ -182,8 +184,9 @@
Message response
= obtainMessage(EVENT_GET_EF_LINEAR_RECORD_SIZE_DONE,
new LoadLinearFixedContext(fileid, onLoaded));
- phone.mCM.iccIO(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),
- 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, response);
+ phone.mCM.iccIOForApp(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),
+ 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, phone.getIccCard().getAid(),
+ response);
}
/**
@@ -199,8 +202,9 @@
Message response = obtainMessage(EVENT_GET_RECORD_SIZE_DONE,
new LoadLinearFixedContext(fileid,onLoaded));
- phone.mCM.iccIO(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),
- 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, response);
+ phone.mCM.iccIOForApp(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),
+ 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null,
+ phone.getIccCard().getAid(), response);
}
/**
@@ -217,8 +221,9 @@
Message response = obtainMessage(EVENT_GET_BINARY_SIZE_DONE,
fileid, 0, onLoaded);
- phone.mCM.iccIO(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),
- 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, response);
+ phone.mCM.iccIOForApp(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),
+ 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null,
+ phone.getIccCard().getAid(), response);
}
/**
@@ -236,8 +241,8 @@
Message response = obtainMessage(EVENT_READ_ICON_DONE, fileid, 0,
onLoaded);
- phone.mCM.iccIO(COMMAND_READ_BINARY, fileid, "img", highOffset, lowOffset,
- length, null, null, response);
+ phone.mCM.iccIOForApp(COMMAND_READ_BINARY, fileid, "img", highOffset, lowOffset,
+ length, null, null, phone.getIccCard().getAid(), response);
}
/**
@@ -251,9 +256,10 @@
*/
public void updateEFLinearFixed(int fileid, int recordNum, byte[] data,
String pin2, Message onComplete) {
- phone.mCM.iccIO(COMMAND_UPDATE_RECORD, fileid, getEFPath(fileid),
+ phone.mCM.iccIOForApp(COMMAND_UPDATE_RECORD, fileid, getEFPath(fileid),
recordNum, READ_RECORD_MODE_ABSOLUTE, data.length,
- IccUtils.bytesToHexString(data), pin2, onComplete);
+ IccUtils.bytesToHexString(data), pin2,
+ phone.getIccCard().getAid(), onComplete);
}
/**
@@ -262,9 +268,10 @@
* @param data must be exactly as long as the EF
*/
public void updateEFTransparent(int fileid, byte[] data, Message onComplete) {
- phone.mCM.iccIO(COMMAND_UPDATE_BINARY, fileid, getEFPath(fileid),
+ phone.mCM.iccIOForApp(COMMAND_UPDATE_BINARY, fileid, getEFPath(fileid),
0, 0, data.length,
- IccUtils.bytesToHexString(data), null, onComplete);
+ IccUtils.bytesToHexString(data), null,
+ phone.getIccCard().getAid(), onComplete);
}
@@ -395,10 +402,11 @@
lc.results = new ArrayList<byte[]>(lc.countRecords);
}
- phone.mCM.iccIO(COMMAND_READ_RECORD, lc.efid, getEFPath(lc.efid),
+ phone.mCM.iccIOForApp(COMMAND_READ_RECORD, lc.efid, getEFPath(lc.efid),
lc.recordNum,
READ_RECORD_MODE_ABSOLUTE,
lc.recordSize, null, null,
+ phone.getIccCard().getAid(),
obtainMessage(EVENT_READ_RECORD_DONE, lc));
break;
case EVENT_GET_BINARY_SIZE_DONE:
@@ -433,8 +441,9 @@
size = ((data[RESPONSE_DATA_FILE_SIZE_1] & 0xff) << 8)
+ (data[RESPONSE_DATA_FILE_SIZE_2] & 0xff);
- phone.mCM.iccIO(COMMAND_READ_BINARY, fileid, getEFPath(fileid),
+ phone.mCM.iccIOForApp(COMMAND_READ_BINARY, fileid, getEFPath(fileid),
0, 0, size, null, null,
+ phone.getIccCard().getAid(),
obtainMessage(EVENT_READ_BINARY_DONE,
fileid, 0, response));
break;
@@ -468,10 +477,11 @@
if (lc.recordNum > lc.countRecords) {
sendResult(response, lc.results, null);
} else {
- phone.mCM.iccIO(COMMAND_READ_RECORD, lc.efid, getEFPath(lc.efid),
+ phone.mCM.iccIOForApp(COMMAND_READ_RECORD, lc.efid, getEFPath(lc.efid),
lc.recordNum,
READ_RECORD_MODE_ABSOLUTE,
lc.recordSize, null, null,
+ phone.getIccCard().getAid(),
obtainMessage(EVENT_READ_RECORD_DONE, lc));
}
}
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index 156eb32..f587fe1 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -880,9 +880,19 @@
public void
getIMSI(Message result) {
+ getIMSIForApp(null, result);
+ }
+
+ public void
+ getIMSIForApp(String aid, Message result) {
RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_IMSI, result);
- if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+ rr.mp.writeInt(1);
+ rr.mp.writeString(aid);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() +
+ "> getIMSI: " + requestToString(rr.mRequest)
+ + " aid: " + aid);
send(rr);
}
@@ -1435,6 +1445,11 @@
public void
iccIO (int command, int fileid, String path, int p1, int p2, int p3,
String data, String pin2, Message result) {
+ iccIOForApp(command, fileid, path, p1, p2, p3, data, pin2, null, result);
+ }
+ public void
+ iccIOForApp (int command, int fileid, String path, int p1, int p2, int p3,
+ String data, String pin2, String aid, Message result) {
//Note: This RIL request has not been renamed to ICC,
// but this request is also valid for SIM and RUIM
RILRequest rr
@@ -1448,12 +1463,15 @@
rr.mp.writeInt(p3);
rr.mp.writeString(data);
rr.mp.writeString(pin2);
+ rr.mp.writeString(aid);
- if (RILJ_LOGD) riljLog(rr.serialString() + "> iccIO: " + requestToString(rr.mRequest)
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> iccIO: "
+ + requestToString(rr.mRequest)
+ " 0x" + Integer.toHexString(command)
+ " 0x" + Integer.toHexString(fileid) + " "
+ " path: " + path + ","
- + p1 + "," + p2 + "," + p3);
+ + p1 + "," + p2 + "," + p3
+ + " aid: " + aid);
send(rr);
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccFileHandler.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccFileHandler.java
index b57af0e..8375fd0 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccFileHandler.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccFileHandler.java
@@ -55,8 +55,8 @@
if (fileid == EF_CSIM_EPRL) {
// Entire PRL could be huge. We are only interested in
// the first 4 bytes of the record.
- phone.mCM.iccIO(COMMAND_READ_BINARY, fileid, getEFPath(fileid),
- 0, 0, 4, null, null,
+ phone.mCM.iccIOForApp(COMMAND_READ_BINARY, fileid, getEFPath(fileid),
+ 0, 0, 4, null, null, phone.getIccCard().getAid(),
obtainMessage(EVENT_READ_BINARY_DONE,
fileid, 0, onLoaded));
} else {
diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimFileHandler.java b/telephony/java/com/android/internal/telephony/cdma/RuimFileHandler.java
index 3e2a29b..375cc07 100644
--- a/telephony/java/com/android/internal/telephony/cdma/RuimFileHandler.java
+++ b/telephony/java/com/android/internal/telephony/cdma/RuimFileHandler.java
@@ -57,8 +57,9 @@
Message response = obtainMessage(EVENT_READ_ICON_DONE, fileid, 0,
onLoaded);
- phone.mCM.iccIO(COMMAND_GET_RESPONSE, fileid, "img", 0, 0,
- GET_RESPONSE_EF_IMG_SIZE_BYTES, null, null, response);
+ phone.mCM.iccIOForApp(COMMAND_GET_RESPONSE, fileid, "img", 0, 0,
+ GET_RESPONSE_EF_IMG_SIZE_BYTES, null, null,
+ phone.getIccCard().getAid(), response);
}
@Override
diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
index b2fa051..de8401e 100755
--- a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
@@ -1309,7 +1309,7 @@
logv("fetchSimRecords " + recordsToLoad);
- phone.mCM.getIMSI(obtainMessage(EVENT_GET_IMSI_DONE));
+ phone.mCM.getIMSIForApp(phone.getIccCard().getAid(), obtainMessage(EVENT_GET_IMSI_DONE));
recordsToLoad++;
iccFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE));
diff --git a/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java b/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java
index 9201984..99f4e0f 100644
--- a/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java
@@ -84,6 +84,9 @@
public void getIMSI(Message result) {
}
+ public void getIMSIForApp(String aid, Message result) {
+ }
+
public void getIMEI(Message result) {
}
@@ -213,6 +216,9 @@
public void iccIO (int command, int fileid, String path, int p1, int p2,
int p3, String data, String pin2, Message result) {
}
+ public void iccIOForApp (int command, int fileid, String path, int p1, int p2,
+ int p3, String data, String pin2, String aid, Message result) {
+ }
public void getCLIR(Message result) {
}
diff --git a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
index 60d9d24..4f61509 100644
--- a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
+++ b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
@@ -504,6 +504,9 @@
resultSuccess(result, null);
}
+ public void getIMSI(Message result) {
+ getIMSIForApp(null, result);
+ }
/**
* returned message
* retMsg.obj = AsyncResult ar
@@ -511,7 +514,7 @@
* ar.userObject contains the original value of result.obj
* ar.result is String containing IMSI on success
*/
- public void getIMSI(Message result) {
+ public void getIMSIForApp(String aid, Message result) {
resultSuccess(result, "012345678901234");
}
@@ -1042,13 +1045,18 @@
unimplemented(result);
}
+ public void iccIO(int command, int fileid, String path, int p1, int p2, int p3, String data,
+ String pin2, Message response) {
+ iccIOForApp(command, fileid, path, p1, p2, p3, data,pin2, null, response);
+ }
+
/**
* parameters equivalent to 27.007 AT+CRSM command
* response.obj will be an AsyncResult
* response.obj.userObj will be a SimIoResult on success
*/
- public void iccIO (int command, int fileid, String path, int p1, int p2,
- int p3, String data, String pin2, Message result) {
+ public void iccIOForApp (int command, int fileid, String path, int p1, int p2,
+ int p3, String data, String pin2, String aid, Message result) {
unimplemented(result);
}
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java
index b385cee..ea6836d 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java
@@ -612,4 +612,13 @@
@Override
public void getVoiceRadioTechnology(Message response) {
}
+
+ @Override
+ public void getIMSIForApp(String aid, Message result) {
+ }
+
+ @Override
+ public void iccIOForApp(int command, int fileid, String path, int p1, int p2, int p3,
+ String data, String pin2, String aid, Message response) {
+ }
}
diff --git a/test-runner/src/android/test/mock/MockContentProvider.java b/test-runner/src/android/test/mock/MockContentProvider.java
index 4ff943e..a8c388e 100644
--- a/test-runner/src/android/test/mock/MockContentProvider.java
+++ b/test-runner/src/android/test/mock/MockContentProvider.java
@@ -21,7 +21,7 @@
import android.content.ContentProviderResult;
import android.content.ContentValues;
import android.content.Context;
-import android.content.ICancelationSignal;
+import android.content.ICancellationSignal;
import android.content.IContentProvider;
import android.content.OperationApplicationException;
import android.content.pm.PathPermission;
@@ -93,7 +93,7 @@
@Override
public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs,
- String sortOrder, ICancelationSignal cancelationSignal) throws RemoteException {
+ String sortOrder, ICancellationSignal cancellationSignal) throws RemoteException {
return MockContentProvider.this.query(url, projection, selection,
selectionArgs, sortOrder);
}
@@ -127,7 +127,7 @@
}
@Override
- public ICancelationSignal createCancelationSignal() throws RemoteException {
+ public ICancellationSignal createCancellationSignal() throws RemoteException {
return null;
}
}
diff --git a/test-runner/src/android/test/mock/MockIContentProvider.java b/test-runner/src/android/test/mock/MockIContentProvider.java
index 41bc27d..1aa0448 100644
--- a/test-runner/src/android/test/mock/MockIContentProvider.java
+++ b/test-runner/src/android/test/mock/MockIContentProvider.java
@@ -21,7 +21,7 @@
import android.content.ContentValues;
import android.content.EntityIterator;
import android.content.IContentProvider;
-import android.content.ICancelationSignal;
+import android.content.ICancellationSignal;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.net.Uri;
@@ -73,7 +73,7 @@
}
public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs,
- String sortOrder, ICancelationSignal cancelationSignal) {
+ String sortOrder, ICancellationSignal cancellationSignal) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@@ -106,7 +106,7 @@
}
@Override
- public ICancelationSignal createCancelationSignal() throws RemoteException {
+ public ICancellationSignal createCancellationSignal() throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
}
diff --git a/tests/BiDiTests/res/layout/basic.xml b/tests/BiDiTests/res/layout/basic.xml
index f503658..7d4d4db 100644
--- a/tests/BiDiTests/res/layout/basic.xml
+++ b/tests/BiDiTests/res/layout/basic.xml
@@ -19,6 +19,10 @@
android:layout_width="fill_parent"
android:layout_height="fill_parent">
+ <ScrollView
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
<LinearLayout android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -131,4 +135,6 @@
</LinearLayout>
+ </ScrollView>
+
</FrameLayout>
diff --git a/tests/BiDiTests/res/values/strings.xml b/tests/BiDiTests/res/values/strings.xml
index 1d4fc84..233cd0d 100644
--- a/tests/BiDiTests/res/values/strings.xml
+++ b/tests/BiDiTests/res/values/strings.xml
@@ -24,11 +24,11 @@
<string name="button_before_text">Start</string>
<string name="button_requestlayout_text">Request Layout</string>
<string name="button_alert_dialog_text">AlertDialog</string>
- <string name="textview_text">This is a text for a TextView</string>
- <string name="textview_ltr_text">This is a text for a LTR TextView</string>
- <string name="textview_rtl_text">This is a text for a RTL TextView</string>
- <string name="textview_default_text">This is a text for a default TextView</string>
- <string name="textview_password_default_text">This is a text for a password TextView</string>
+ <string name="textview_text">TextView</string>
+ <string name="textview_ltr_text">LTR TextView</string>
+ <string name="textview_rtl_text">RTL TextView</string>
+ <string name="textview_default_text">Default TextView</string>
+ <string name="textview_password_default_text">Password TextView</string>
<string name="edittext_text">mmmmmmmmmmmmmmmmmmmmmmmm</string>
<string name="normal_text">Normal String</string>
<string name="normal_long_text">mmmmmmmmmmmmmmmmmmmmmmmm</string>
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 5bbcce3..643cb8d 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -30,6 +30,8 @@
android:label="HwUi"
android:hardwareAccelerated="true">
+ <meta-data android:name="android.graphics.renderThread" android:value="true" />
+
<activity
android:name="PaintDrawFilterActivity"
android:label="_DrawFilter">
diff --git a/tests/touchlag/Android.mk b/tests/touchlag/Android.mk
new file mode 100644
index 0000000..4f8aa1e
--- /dev/null
+++ b/tests/touchlag/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ touchlag.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils libutils \
+
+LOCAL_MODULE:= test-touchlag
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
diff --git a/tests/touchlag/touchlag.cpp b/tests/touchlag/touchlag.cpp
new file mode 100644
index 0000000..df4befb
--- /dev/null
+++ b/tests/touchlag/touchlag.cpp
@@ -0,0 +1,294 @@
+/*
+ * 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.
+ */
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/fb.h>
+#include <linux/input.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <cutils/memory.h>
+#include <asm-generic/mman.h>
+#include <sys/mman.h>
+#include <utils/threads.h>
+#include <unistd.h>
+#include <math.h>
+
+using namespace android;
+
+#ifndef FBIO_WAITFORVSYNC
+#define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32)
+#endif
+
+struct Buffer {
+ size_t w;
+ size_t h;
+ size_t s;
+ union {
+ void* addr;
+ uint32_t* pixels;
+ };
+};
+
+void clearBuffer(Buffer* buf, uint32_t pixel) {
+ android_memset32(buf->pixels, pixel, buf->s * buf->h * 4);
+}
+
+void drawTwoPixels(Buffer* buf, uint32_t pixel, ssize_t x, ssize_t y, size_t w) {
+ if (y>0 && y<ssize_t(buf->h)) {
+ uint32_t* bits = buf->pixels + y * buf->s;
+ if (x>=0 && x<buf->w) {
+ bits[x] = pixel;
+ }
+ ssize_t W(w);
+ if ((x+W)>=0 && (x+W)<buf->w) {
+ bits[x+W] = pixel;
+ }
+ }
+}
+
+void drawHLine(Buffer* buf, uint32_t pixel, ssize_t x, ssize_t y, size_t w) {
+ if (y>0 && y<ssize_t(buf->h)) {
+ ssize_t W(w);
+ if (x<0) {
+ W += x;
+ x = 0;
+ }
+ if (x+w > buf->w) {
+ W = buf->w - x;
+ }
+ if (W>0) {
+ uint32_t* bits = buf->pixels + y * buf->s + x;
+ android_memset32(bits, pixel, W*4);
+ }
+ }
+}
+
+void drawRect(Buffer* buf, uint32_t pixel, ssize_t x, ssize_t y, size_t w, size_t h) {
+ ssize_t W(w), H(h);
+ if (x<0) {
+ w += x;
+ x = 0;
+ }
+ if (y<0) {
+ h += y;
+ y = 0;
+ }
+ if (x+w > buf->w) W = buf->w - x;
+ if (y+h > buf->h) H = buf->h - y;
+ if (W>0 && H>0) {
+ uint32_t* bits = buf->pixels + y * buf->s + x;
+ for (ssize_t i=0 ; i<H ; i++) {
+ android_memset32(bits, pixel, W*4);
+ bits += buf->s;
+ }
+ }
+}
+
+void drawCircle(Buffer* buf, uint32_t pixel,
+ size_t x0, size_t y0, size_t radius, bool filled = false) {
+ ssize_t f = 1 - radius;
+ ssize_t ddF_x = 1;
+ ssize_t ddF_y = -2 * radius;
+ ssize_t x = 0;
+ ssize_t y = radius;
+ if (filled) {
+ drawHLine(buf, pixel, x0-radius, y0, 2*radius);
+ } else {
+ drawTwoPixels(buf, pixel, x0-radius, y0, 2*radius);
+ }
+ while (x < y) {
+ if (f >= 0) {
+ y--;
+ ddF_y += 2;
+ f += ddF_y;
+ }
+ x++;
+ ddF_x += 2;
+ f += ddF_x;
+ if (filled) {
+ drawHLine(buf, pixel, x0-x, y0+y, 2*x);
+ drawHLine(buf, pixel, x0-x, y0-y, 2*x);
+ drawHLine(buf, pixel, x0-y, y0+x, 2*y);
+ drawHLine(buf, pixel, x0-y, y0-x, 2*y);
+ } else {
+ drawTwoPixels(buf, pixel, x0-x, y0+y, 2*x);
+ drawTwoPixels(buf, pixel, x0-x, y0-y, 2*x);
+ drawTwoPixels(buf, pixel, x0-y, y0+x, 2*y);
+ drawTwoPixels(buf, pixel, x0-y, y0-x, 2*y);
+ }
+ }
+}
+
+class TouchEvents {
+ class EventThread : public Thread {
+ int fd;
+
+ virtual bool threadLoop() {
+ input_event event;
+ int first_down = 0;
+ do {
+ read(fd, &event, sizeof(event));
+ if (event.type == EV_ABS) {
+ if (event.code == ABS_MT_TRACKING_ID) {
+ down = event.value == -1 ? 0 : 1;
+ first_down = down;
+ }
+ if (event.code == ABS_MT_POSITION_X) {
+ x = event.value;
+ }
+ if (event.code == ABS_MT_POSITION_Y) {
+ y = event.value;
+ }
+ }
+ } while (event.type == EV_SYN);
+ return true;
+ }
+
+ public:
+ int x, y, down;
+ EventThread() : Thread(false),
+ x(0), y(0), down(0)
+ {
+ fd = open("/dev/input/event1", O_RDONLY);
+ }
+};
+ sp<EventThread> thread;
+
+public:
+ TouchEvents() {
+ thread = new EventThread();
+ thread->run("EventThread", PRIORITY_URGENT_DISPLAY);
+ }
+
+ int getMostRecentPosition(int* x, int* y) {
+ *x = thread->x;
+ *y = thread->y;
+ return thread->down;
+ }
+};
+
+
+struct Queue {
+ struct position {
+ int x, y;
+ };
+ int index;
+ position q[16];
+ Queue() : index(0) { }
+ void push(int x, int y) {
+ index++;
+ index &= 0xF;
+ q[index].x = x;
+ q[index].y = y;
+ }
+ void get(int lag, int* x, int* y) {
+ const int i = (index - lag) & 0xF;
+ *x = q[i].x;
+ *y = q[i].y;
+ }
+};
+
+extern char *optarg;
+extern int optind;
+extern int optopt;
+extern int opterr;
+extern int optreset;
+
+void usage(const char* name) {
+ printf("\nusage: %s [-h] [-l lag]\n", name);
+}
+
+int main(int argc, char** argv) {
+ fb_var_screeninfo vi;
+ fb_fix_screeninfo fi;
+
+ int lag = 0;
+ int fd = open("/dev/graphics/fb0", O_RDWR);
+ ioctl(fd, FBIOGET_VSCREENINFO, &vi);
+ ioctl(fd, FBIOGET_FSCREENINFO, &fi);
+ void* bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ Buffer framebuffer;
+ framebuffer.w = vi.xres;
+ framebuffer.h = vi.yres;
+ framebuffer.s = fi.line_length / (vi.bits_per_pixel >> 3);
+ framebuffer.addr = bits;
+
+ int ch;
+ while ((ch = getopt(argc, argv, "hl:")) != -1) {
+ switch (ch) {
+ case 'l':
+ lag = atoi(optarg);
+ break;
+ case 'h':
+ default:
+ usage(argv[0]);
+ exit(0);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+
+ TouchEvents touch;
+ Queue queue;
+
+
+ int x=0, y=0, down=0;
+ int lag_x=0, lag_y=0;
+
+ clearBuffer(&framebuffer, 0);
+ while (true) {
+ uint32_t crt = 0;
+ int err = ioctl(fd, FBIO_WAITFORVSYNC, &crt);
+
+ // draw beam marker
+ drawRect(&framebuffer, 0x400000, framebuffer.w-2, 0, 2, framebuffer.h);
+ // erase screen
+ if (lag) {
+ drawCircle(&framebuffer, 0, lag_x, lag_y, 100);
+ drawHLine(&framebuffer, 0, 0, lag_y, 32);
+ }
+ drawCircle(&framebuffer, 0, x, y, 100, true);
+ drawHLine(&framebuffer, 0, 0, y, 32);
+
+ // draw a line at y=1000
+ drawHLine(&framebuffer, 0x808080, 0, 1000, framebuffer.w);
+
+ // get touch events
+ touch.getMostRecentPosition(&x, &y);
+ queue.push(x, y);
+ queue.get(lag, &lag_x, &lag_y);
+
+ if (lag) {
+ drawCircle(&framebuffer, 0x00FF00, lag_x, lag_y, 100);
+ drawHLine(&framebuffer, 0x00FF00, 0, lag_y, 32);
+ }
+
+ drawCircle(&framebuffer, 0xFFFFFF, x, y, 100, true);
+ drawHLine(&framebuffer, 0xFFFFFF, 0, y, 32);
+
+ // draw end of frame beam marker
+ drawRect(&framebuffer, 0x004000, framebuffer.w-2, 0, 2, framebuffer.h);
+ }
+
+ close(fd);
+ return 0;
+}
diff --git a/tools/layoutlib/bridge/.settings/README.txt b/tools/layoutlib/bridge/.settings/README.txt
new file mode 100644
index 0000000..9120b20
--- /dev/null
+++ b/tools/layoutlib/bridge/.settings/README.txt
@@ -0,0 +1,2 @@
+Copy this in eclipse project as a .settings folder at the root.
+This ensure proper compilation compliance and warning/error levels.
\ No newline at end of file
diff --git a/tools/layoutlib/bridge/.settings/org.eclipse.jdt.core.prefs b/tools/layoutlib/bridge/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..5381a0e
--- /dev/null
+++ b/tools/layoutlib/bridge/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,93 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.annotation.nonnull=com.android.annotations.NonNull
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=com.android.annotations.NonNullByDefault
+org.eclipse.jdt.core.compiler.annotation.nonnullisdefault=disabled
+org.eclipse.jdt.core.compiler.annotation.nullable=com.android.annotations.Nullable
+org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=enabled
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.nullSpecInsufficientInfo=warning
+org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=warning
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.unclosedCloseable=error
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/tools/layoutlib/bridge/src/android/animation/AnimationThread.java b/tools/layoutlib/bridge/src/android/animation/AnimationThread.java
index af83c61..b46134a 100644
--- a/tools/layoutlib/bridge/src/android/animation/AnimationThread.java
+++ b/tools/layoutlib/bridge/src/android/animation/AnimationThread.java
@@ -23,11 +23,10 @@
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.RenderSessionImpl;
-import android.animation.ValueAnimator;
import android.os.Handler;
import android.os.Handler_Delegate;
-import android.os.Message;
import android.os.Handler_Delegate.IHandlerCallback;
+import android.os.Message;
import java.util.PriorityQueue;
import java.util.Queue;
@@ -57,6 +56,7 @@
mUptimeMillis = uptimeMillis;
}
+ @Override
public int compareTo(MessageBundle bundle) {
if (mUptimeMillis < bundle.mUptimeMillis) {
return -1;
@@ -85,6 +85,7 @@
Bridge.prepareThread();
try {
Handler_Delegate.setCallback(new IHandlerCallback() {
+ @Override
public void sendMessageAtTime(Handler handler, Message msg, long uptimeMillis) {
if (msg.what == ValueAnimator.ANIMATION_START /*||
FIXME: The ANIMATION_FRAME message no longer exists. Instead,
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java
index 9a8cf04..65a75b0 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java
@@ -105,6 +105,7 @@
mTileModeY = tileModeY;
}
+ @Override
public java.awt.PaintContext createContext(
java.awt.image.ColorModel colorModel,
java.awt.Rectangle deviceBounds,
@@ -148,13 +149,16 @@
mColorModel = colorModel;
}
+ @Override
public void dispose() {
}
+ @Override
public java.awt.image.ColorModel getColorModel() {
return mColorModel;
}
+ @Override
public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
java.awt.image.BufferedImage.TYPE_INT_ARGB);
@@ -240,6 +244,7 @@
}
+ @Override
public int getTransparency() {
return java.awt.Paint.TRANSLUCENT;
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index f797836..16f1575 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -291,6 +291,7 @@
Paint paint) {
draw(thisCanvas.mNativeCanvas, paint.mNativePaint, false /*compositeOnly*/,
false /*forceSrcMode*/, new GcSnapshot.Drawable() {
+ @Override
public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
for (int i = 0 ; i < count ; i += 4) {
graphics.drawLine((int)pts[i + offset], (int)pts[i + offset + 1],
@@ -619,6 +620,7 @@
final int h = canvasDelegate.mBitmap.getImage().getHeight();
draw(nativeCanvas, new GcSnapshot.Drawable() {
+ @Override
public void draw(Graphics2D graphics, Paint_Delegate paint) {
// reset its transform just in case
graphics.setTransform(new AffineTransform());
@@ -651,6 +653,7 @@
draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
new GcSnapshot.Drawable() {
+ @Override
public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
graphics.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY);
}
@@ -669,6 +672,7 @@
draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
new GcSnapshot.Drawable() {
+ @Override
public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
int style = paintDelegate.getStyle();
@@ -693,6 +697,7 @@
if (oval.right > oval.left && oval.bottom > oval.top) {
draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
new GcSnapshot.Drawable() {
+ @Override
public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
int style = paintDelegate.getStyle();
@@ -728,6 +733,7 @@
if (oval.right > oval.left && oval.bottom > oval.top) {
draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
new GcSnapshot.Drawable() {
+ @Override
public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
int style = paintDelegate.getStyle();
@@ -757,6 +763,7 @@
draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
new GcSnapshot.Drawable() {
+ @Override
public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
int style = paintDelegate.getStyle();
@@ -789,6 +796,7 @@
draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
new GcSnapshot.Drawable() {
+ @Override
public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
Shape shape = pathDelegate.getJavaShape();
int style = paintDelegate.getStyle();
@@ -892,6 +900,7 @@
draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, false /*forceSrcMode*/,
new GcSnapshot.Drawable() {
+ @Override
public void draw(Graphics2D graphics, Paint_Delegate paint) {
if (paint != null && paint.isFilterBitmap()) {
graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
@@ -931,6 +940,7 @@
final AffineTransform mtx = matrixDelegate.getAffineTransform();
canvasDelegate.getSnapshot().draw(new GcSnapshot.Drawable() {
+ @Override
public void draw(Graphics2D graphics, Paint_Delegate paint) {
if (paint != null && paint.isFilterBitmap()) {
graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
@@ -970,6 +980,7 @@
final float startX, final float startY, int flags, int paint) {
draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
new GcSnapshot.Drawable() {
+ @Override
public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
// WARNING: the logic in this method is similar to Paint_Delegate.measureText.
// Any change to this method should be reflected in Paint.measureText
@@ -1279,6 +1290,7 @@
draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, sBoolOut[0],
new GcSnapshot.Drawable() {
+ @Override
public void draw(Graphics2D graphics, Paint_Delegate paint) {
if (paint != null && paint.isFilterBitmap()) {
graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
diff --git a/tools/layoutlib/bridge/src/android/graphics/Gradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Gradient_Delegate.java
index 38c092d..7475c22 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Gradient_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Gradient_Delegate.java
@@ -87,6 +87,7 @@
mTileMode = tileMode;
}
+ @Override
public int getTransparency() {
return java.awt.Paint.TRANSLUCENT;
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java
index a2ba758..f117fca 100644
--- a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java
@@ -132,6 +132,7 @@
mDSize2 = mDx * mDx + mDy * mDy;
}
+ @Override
public java.awt.PaintContext createContext(
java.awt.image.ColorModel colorModel,
java.awt.Rectangle deviceBounds,
@@ -176,13 +177,16 @@
mColorModel = colorModel;
}
+ @Override
public void dispose() {
}
+ @Override
public java.awt.image.ColorModel getColorModel() {
return mColorModel;
}
+ @Override
public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
java.awt.image.BufferedImage.TYPE_INT_ARGB);
diff --git a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
index 5e882ce..be27b54 100644
--- a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
@@ -215,6 +215,7 @@
Paint_Delegate paint_delegate = Paint_Delegate.getDelegate(paint_instance_or_null);
canvas_delegate.getSnapshot().draw(new GcSnapshot.Drawable() {
+ @Override
public void draw(Graphics2D graphics, Paint_Delegate paint) {
chunkObject.draw(bitmap_delegate.getImage(), graphics,
left, top, right - left, bottom - top, destDensity, srcDensity);
diff --git a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java
index 9bf78b4..3fe45fa 100644
--- a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java
@@ -118,6 +118,7 @@
mRadius = radius;
}
+ @Override
public java.awt.PaintContext createContext(
java.awt.image.ColorModel colorModel,
java.awt.Rectangle deviceBounds,
@@ -162,13 +163,16 @@
mColorModel = colorModel;
}
+ @Override
public void dispose() {
}
+ @Override
public java.awt.image.ColorModel getColorModel() {
return mColorModel;
}
+ @Override
public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
java.awt.image.BufferedImage.TYPE_INT_ARGB);
diff --git a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java
index 966e06e..13ae12e 100644
--- a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java
@@ -110,6 +110,7 @@
mCy = cy;
}
+ @Override
public java.awt.PaintContext createContext(
java.awt.image.ColorModel colorModel,
java.awt.Rectangle deviceBounds,
@@ -154,13 +155,16 @@
mColorModel = colorModel;
}
+ @Override
public void dispose() {
}
+ @Override
public java.awt.image.ColorModel getColorModel() {
return mColorModel;
}
+ @Override
public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
java.awt.image.BufferedImage.TYPE_INT_ARGB);
diff --git a/tools/layoutlib/bridge/src/android/view/SurfaceView.java b/tools/layoutlib/bridge/src/android/view/SurfaceView.java
index ce32da9..6aa4b3b 100644
--- a/tools/layoutlib/bridge/src/android/view/SurfaceView.java
+++ b/tools/layoutlib/bridge/src/android/view/SurfaceView.java
@@ -27,7 +27,7 @@
* Mock version of the SurfaceView.
* Only non override public methods from the real SurfaceView have been added in there.
* Methods that take an unknown class as parameter or as return object, have been removed for now.
- *
+ *
* TODO: generate automatically.
*
*/
@@ -36,7 +36,7 @@
public SurfaceView(Context context) {
this(context, null);
}
-
+
public SurfaceView(Context context, AttributeSet attrs) {
this(context, attrs , 0);
}
@@ -44,53 +44,66 @@
public SurfaceView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
-
+
public SurfaceHolder getHolder() {
return mSurfaceHolder;
}
private SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
-
+
+ @Override
public boolean isCreating() {
return false;
}
+ @Override
public void addCallback(Callback callback) {
}
+ @Override
public void removeCallback(Callback callback) {
}
-
+
+ @Override
public void setFixedSize(int width, int height) {
}
+ @Override
public void setSizeFromLayout() {
}
+ @Override
public void setFormat(int format) {
}
+ @Override
public void setType(int type) {
}
+ @Override
public void setKeepScreenOn(boolean screenOn) {
}
-
+
+ @Override
public Canvas lockCanvas() {
return null;
}
+ @Override
public Canvas lockCanvas(Rect dirty) {
return null;
}
+ @Override
public void unlockCanvasAndPost(Canvas canvas) {
}
+ @Override
public Surface getSurface() {
return null;
}
+ @Override
public Rect getSurfaceFrame() {
return null;
}
diff --git a/tools/layoutlib/bridge/src/com/android/internal/textservice/ITextServicesManager_Stub_Delegate.java b/tools/layoutlib/bridge/src/com/android/internal/textservice/ITextServicesManager_Stub_Delegate.java
index 9efdcaf..3017292 100644
--- a/tools/layoutlib/bridge/src/com/android/internal/textservice/ITextServicesManager_Stub_Delegate.java
+++ b/tools/layoutlib/bridge/src/com/android/internal/textservice/ITextServicesManager_Stub_Delegate.java
@@ -43,28 +43,33 @@
private static class FakeTextServicesManager implements ITextServicesManager {
+ @Override
public void finishSpellCheckerService(ISpellCheckerSessionListener arg0)
throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public SpellCheckerInfo getCurrentSpellChecker(String arg0) throws RemoteException {
// TODO Auto-generated method stub
return null;
}
+ @Override
public SpellCheckerSubtype getCurrentSpellCheckerSubtype(String arg0, boolean arg1)
throws RemoteException {
// TODO Auto-generated method stub
return null;
}
+ @Override
public SpellCheckerInfo[] getEnabledSpellCheckers() throws RemoteException {
// TODO Auto-generated method stub
return null;
}
+ @Override
public void getSpellCheckerService(String arg0, String arg1,
ITextServicesSessionListener arg2, ISpellCheckerSessionListener arg3, Bundle arg4)
throws RemoteException {
@@ -72,26 +77,31 @@
}
+ @Override
public boolean isSpellCheckerEnabled() throws RemoteException {
// TODO Auto-generated method stub
return false;
}
+ @Override
public void setCurrentSpellChecker(String arg0, String arg1) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void setCurrentSpellCheckerSubtype(String arg0, int arg1) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void setSpellCheckerEnabled(boolean arg0) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public IBinder asBinder() {
// TODO Auto-generated method stub
return null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
index c64ab65..e28866e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
@@ -19,7 +19,7 @@
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentValues;
-import android.content.ICancelationSignal;
+import android.content.ICancellationSignal;
import android.content.IContentProvider;
import android.content.OperationApplicationException;
import android.content.res.AssetFileDescriptor;
@@ -92,7 +92,7 @@
@Override
public Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3, String arg4,
- ICancelationSignal arg5) throws RemoteException {
+ ICancellationSignal arg5) throws RemoteException {
// TODO Auto-generated method stub
return null;
}
@@ -124,7 +124,7 @@
}
@Override
- public ICancelationSignal createCancelationSignal() throws RemoteException {
+ public ICancellationSignal createCancellationSignal() throws RemoteException {
// TODO Auto-generated method stub
return null;
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
index 2a52888..db0694c 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
@@ -37,151 +37,179 @@
*/
public class BridgeIInputMethodManager implements IInputMethodManager {
+ @Override
public void addClient(IInputMethodClient arg0, IInputContext arg1, int arg2, int arg3)
throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void finishInput(IInputMethodClient arg0) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public InputMethodSubtype getCurrentInputMethodSubtype() throws RemoteException {
// TODO Auto-generated method stub
return null;
}
+ @Override
public List<InputMethodInfo> getEnabledInputMethodList() throws RemoteException {
// TODO Auto-generated method stub
return null;
}
+ @Override
public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(InputMethodInfo arg0,
boolean arg1) throws RemoteException {
// TODO Auto-generated method stub
return null;
}
+ @Override
public List<InputMethodInfo> getInputMethodList() throws RemoteException {
// TODO Auto-generated method stub
return null;
}
+ @Override
public InputMethodSubtype getLastInputMethodSubtype() throws RemoteException {
// TODO Auto-generated method stub
return null;
}
+ @Override
public List getShortcutInputMethodsAndSubtypes() throws RemoteException {
// TODO Auto-generated method stub
return null;
}
+ @Override
public void hideMySoftInput(IBinder arg0, int arg1) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public boolean hideSoftInput(IInputMethodClient arg0, int arg1, ResultReceiver arg2)
throws RemoteException {
// TODO Auto-generated method stub
return false;
}
+ @Override
public boolean notifySuggestionPicked(SuggestionSpan arg0, String arg1, int arg2)
throws RemoteException {
// TODO Auto-generated method stub
return false;
}
+ @Override
public void registerSuggestionSpansForNotification(SuggestionSpan[] arg0)
throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void removeClient(IInputMethodClient arg0) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void setAdditionalInputMethodSubtypes(String arg0, InputMethodSubtype[] arg1)
throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public boolean setCurrentInputMethodSubtype(InputMethodSubtype arg0) throws RemoteException {
// TODO Auto-generated method stub
return false;
}
+ @Override
public void setImeWindowStatus(IBinder arg0, int arg1, int arg2) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void setInputMethod(IBinder arg0, String arg1) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void setInputMethodAndSubtype(IBinder arg0, String arg1, InputMethodSubtype arg2)
throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public boolean setInputMethodEnabled(String arg0, boolean arg1) throws RemoteException {
// TODO Auto-generated method stub
return false;
}
+ @Override
public void showInputMethodAndSubtypeEnablerFromClient(IInputMethodClient arg0, String arg1)
throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void showInputMethodPickerFromClient(IInputMethodClient arg0) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void showMySoftInput(IBinder arg0, int arg1) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public boolean showSoftInput(IInputMethodClient arg0, int arg1, ResultReceiver arg2)
throws RemoteException {
// TODO Auto-generated method stub
return false;
}
+ @Override
public InputBindResult startInput(IInputMethodClient arg0, IInputContext arg1, EditorInfo arg2,
boolean arg3, boolean arg4) throws RemoteException {
// TODO Auto-generated method stub
return null;
}
+ @Override
public boolean switchToLastInputMethod(IBinder arg0) throws RemoteException {
// TODO Auto-generated method stub
return false;
}
+ @Override
public void updateStatusIcon(IBinder arg0, String arg1, int arg2) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void windowGainedFocus(IInputMethodClient arg0, IBinder arg1, boolean arg2,
boolean arg3, int arg4, boolean arg5, int arg6) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public IBinder asBinder() {
// TODO Auto-generated method stub
return null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeLayoutParamsMapAttributes.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeLayoutParamsMapAttributes.java
index d208408..f5912e7 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeLayoutParamsMapAttributes.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeLayoutParamsMapAttributes.java
@@ -37,6 +37,7 @@
mAttributes = attributes;
}
+ @Override
public String getAttributeValue(String namespace, String name) {
if (BridgeConstants.NS_RESOURCES.equals(namespace)) {
return mAttributes.get(name);
@@ -49,93 +50,114 @@
// BridgeContext#obtainStyledAttributes(AttributeSet, int[], int, int)
// Should they ever be called, we'll just implement them on a need basis.
+ @Override
public int getAttributeCount() {
throw new UnsupportedOperationException();
}
+ @Override
public String getAttributeName(int index) {
throw new UnsupportedOperationException();
}
+ @Override
public String getAttributeValue(int index) {
throw new UnsupportedOperationException();
}
+ @Override
public String getPositionDescription() {
throw new UnsupportedOperationException();
}
+ @Override
public int getAttributeNameResource(int index) {
throw new UnsupportedOperationException();
}
+ @Override
public int getAttributeListValue(String namespace, String attribute,
String[] options, int defaultValue) {
throw new UnsupportedOperationException();
}
+ @Override
public boolean getAttributeBooleanValue(String namespace, String attribute,
boolean defaultValue) {
throw new UnsupportedOperationException();
}
+ @Override
public int getAttributeResourceValue(String namespace, String attribute,
int defaultValue) {
throw new UnsupportedOperationException();
}
+ @Override
public int getAttributeIntValue(String namespace, String attribute,
int defaultValue) {
throw new UnsupportedOperationException();
}
+ @Override
public int getAttributeUnsignedIntValue(String namespace, String attribute,
int defaultValue) {
throw new UnsupportedOperationException();
}
+ @Override
public float getAttributeFloatValue(String namespace, String attribute,
float defaultValue) {
throw new UnsupportedOperationException();
}
+ @Override
public int getAttributeListValue(int index,
String[] options, int defaultValue) {
throw new UnsupportedOperationException();
}
+ @Override
public boolean getAttributeBooleanValue(int index, boolean defaultValue) {
throw new UnsupportedOperationException();
}
+ @Override
public int getAttributeResourceValue(int index, int defaultValue) {
throw new UnsupportedOperationException();
}
+ @Override
public int getAttributeIntValue(int index, int defaultValue) {
throw new UnsupportedOperationException();
}
+ @Override
public int getAttributeUnsignedIntValue(int index, int defaultValue) {
throw new UnsupportedOperationException();
}
+ @Override
public float getAttributeFloatValue(int index, float defaultValue) {
throw new UnsupportedOperationException();
}
+ @Override
public String getIdAttribute() {
throw new UnsupportedOperationException();
}
+ @Override
public String getClassAttribute() {
throw new UnsupportedOperationException();
}
+ @Override
public int getIdAttributeResourceValue(int defaultValue) {
throw new UnsupportedOperationException();
}
+ @Override
public int getStyleAttribute() {
throw new UnsupportedOperationException();
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
index e13380e..79606a4 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
@@ -24,72 +24,67 @@
import android.os.RemoteException;
import android.view.DragEvent;
import android.view.IWindow;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
/**
* Implementation of {@link IWindow} to pass to the AttachInfo.
*/
public final class BridgeWindow implements IWindow {
+ @Override
public void dispatchAppVisibility(boolean arg0) throws RemoteException {
// pass for now.
}
+ @Override
public void dispatchGetNewSurface() throws RemoteException {
// pass for now.
}
-
- public void dispatchKey(KeyEvent arg0) throws RemoteException {
- // pass for now.
- }
-
- public void dispatchPointer(MotionEvent arg0, long arg1, boolean arg2) throws RemoteException {
- // pass for now.
- }
-
- public void dispatchTrackball(MotionEvent arg0, long arg1, boolean arg2)
- throws RemoteException {
- // pass for now.
- }
-
+ @Override
public void executeCommand(String arg0, String arg1, ParcelFileDescriptor arg2)
throws RemoteException {
// pass for now.
}
+ @Override
public void resized(int arg0, int arg1, Rect arg2, Rect arg3, boolean arg4, Configuration arg5)
throws RemoteException {
// pass for now.
}
+ @Override
public void windowFocusChanged(boolean arg0, boolean arg1) throws RemoteException {
// pass for now.
}
+ @Override
public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep,
boolean sync) {
// pass for now.
}
+ @Override
public void dispatchWallpaperCommand(String action, int x, int y,
int z, Bundle extras, boolean sync) {
// pass for now.
}
+ @Override
public void closeSystemDialogs(String reason) {
// pass for now.
}
+ @Override
public void dispatchDragEvent(DragEvent event) {
// pass for now.
}
+ @Override
public void dispatchSystemUiVisibilityChanged(int seq, int globalUi,
int localValue, int localChanges) {
// pass for now.
}
+ @Override
public IBinder asBinder() {
// pass for now.
return null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
index 516725e..bef2c95 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
@@ -67,288 +67,345 @@
// ---- implementation of IWindowManager that we care about ----
+ @Override
public int getRotation() throws RemoteException {
return mRotation;
}
+ @Override
public int getMaximumSizeDimension() throws RemoteException {
return 0;
}
+ @Override
public void getDisplaySize(Point arg0) throws RemoteException {
}
+ @Override
public void getRealDisplaySize(Point arg0) throws RemoteException {
}
// ---- unused implementation of IWindowManager ----
+ @Override
public boolean canStatusBarHide() throws RemoteException {
// TODO Auto-generated method stub
return false;
}
+ @Override
public void addAppToken(int arg0, IApplicationToken arg1, int arg2, int arg3, boolean arg4)
throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void addWindowToken(IBinder arg0, int arg1) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void clearForcedDisplaySize() throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void closeSystemDialogs(String arg0) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void disableKeyguard(IBinder arg0, String arg1) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void executeAppTransition() throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void exitKeyguardSecurely(IOnKeyguardExitResult arg0) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void freezeRotation(int arg0) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public float getAnimationScale(int arg0) throws RemoteException {
// TODO Auto-generated method stub
return 0;
}
+ @Override
public float[] getAnimationScales() throws RemoteException {
// TODO Auto-generated method stub
return null;
}
+ @Override
public int getAppOrientation(IApplicationToken arg0) throws RemoteException {
// TODO Auto-generated method stub
return 0;
}
+ @Override
public int getDPadKeycodeState(int arg0) throws RemoteException {
// TODO Auto-generated method stub
return 0;
}
+ @Override
public int getDPadScancodeState(int arg0) throws RemoteException {
// TODO Auto-generated method stub
return 0;
}
+ @Override
public InputDevice getInputDevice(int arg0) throws RemoteException {
// TODO Auto-generated method stub
return null;
}
+ @Override
public int[] getInputDeviceIds() throws RemoteException {
// TODO Auto-generated method stub
return null;
}
+ @Override
public int getKeycodeState(int arg0) throws RemoteException {
// TODO Auto-generated method stub
return 0;
}
+ @Override
public int getKeycodeStateForDevice(int arg0, int arg1) throws RemoteException {
// TODO Auto-generated method stub
return 0;
}
+ @Override
public int getPendingAppTransition() throws RemoteException {
// TODO Auto-generated method stub
return 0;
}
+ @Override
public int getScancodeState(int arg0) throws RemoteException {
// TODO Auto-generated method stub
return 0;
}
+ @Override
public int getScancodeStateForDevice(int arg0, int arg1) throws RemoteException {
// TODO Auto-generated method stub
return 0;
}
+ @Override
public int getSwitchState(int arg0) throws RemoteException {
// TODO Auto-generated method stub
return 0;
}
+ @Override
public int getSwitchStateForDevice(int arg0, int arg1) throws RemoteException {
// TODO Auto-generated method stub
return 0;
}
+ @Override
public int getTrackballKeycodeState(int arg0) throws RemoteException {
// TODO Auto-generated method stub
return 0;
}
+ @Override
public int getTrackballScancodeState(int arg0) throws RemoteException {
// TODO Auto-generated method stub
return 0;
}
+ @Override
public boolean hasKeys(int[] arg0, boolean[] arg1) throws RemoteException {
// TODO Auto-generated method stub
return false;
}
+ @Override
public boolean inKeyguardRestrictedInputMode() throws RemoteException {
// TODO Auto-generated method stub
return false;
}
+ @Override
public boolean injectInputEventNoWait(InputEvent arg0) throws RemoteException {
// TODO Auto-generated method stub
return false;
}
+ @Override
public boolean injectKeyEvent(KeyEvent arg0, boolean arg1) throws RemoteException {
// TODO Auto-generated method stub
return false;
}
+ @Override
public boolean injectPointerEvent(MotionEvent arg0, boolean arg1) throws RemoteException {
// TODO Auto-generated method stub
return false;
}
+ @Override
public boolean injectTrackballEvent(MotionEvent arg0, boolean arg1) throws RemoteException {
// TODO Auto-generated method stub
return false;
}
+ @Override
public boolean inputMethodClientHasFocus(IInputMethodClient arg0) throws RemoteException {
// TODO Auto-generated method stub
return false;
}
+ @Override
public boolean isKeyguardLocked() throws RemoteException {
// TODO Auto-generated method stub
return false;
}
+ @Override
public boolean isKeyguardSecure() throws RemoteException {
// TODO Auto-generated method stub
return false;
}
+ @Override
public boolean isViewServerRunning() throws RemoteException {
// TODO Auto-generated method stub
return false;
}
+ @Override
public InputChannel monitorInput(String arg0) throws RemoteException {
// TODO Auto-generated method stub
return null;
}
+ @Override
public void moveAppToken(int arg0, IBinder arg1) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void moveAppTokensToBottom(List<IBinder> arg0) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void moveAppTokensToTop(List<IBinder> arg0) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public IWindowSession openSession(IInputMethodClient arg0, IInputContext arg1)
throws RemoteException {
// TODO Auto-generated method stub
return null;
}
+ @Override
public void overridePendingAppTransition(String arg0, int arg1, int arg2)
throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void pauseKeyDispatching(IBinder arg0) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void prepareAppTransition(int arg0, boolean arg1) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void reenableKeyguard(IBinder arg0) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void removeAppToken(IBinder arg0) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void removeWindowToken(IBinder arg0) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void resumeKeyDispatching(IBinder arg0) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public Bitmap screenshotApplications(IBinder arg0, int arg1, int arg2) throws RemoteException {
// TODO Auto-generated method stub
return null;
}
+ @Override
public void setAnimationScale(int arg0, float arg1) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void setAnimationScales(float[] arg0) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void setAppGroupId(IBinder arg0, int arg1) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void setAppOrientation(IApplicationToken arg0, int arg1) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void setAppStartingWindow(IBinder arg0, String arg1, int arg2, CompatibilityInfo arg3,
CharSequence arg4, int arg5, int arg6, int arg7, IBinder arg8, boolean arg9)
throws RemoteException {
@@ -356,122 +413,147 @@
}
+ @Override
public void setAppVisibility(IBinder arg0, boolean arg1) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void setAppWillBeHidden(IBinder arg0) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void setEventDispatching(boolean arg0) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void setFocusedApp(IBinder arg0, boolean arg1) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void setForcedDisplaySize(int arg0, int arg1) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void setInTouchMode(boolean arg0) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void setNewConfiguration(Configuration arg0) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void setPointerSpeed(int arg0) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void updateRotation(boolean arg0) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void setStrictModeVisualIndicatorPreference(String arg0) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void showStrictModeViolation(boolean arg0) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void startAppFreezingScreen(IBinder arg0, int arg1) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public boolean startViewServer(int arg0) throws RemoteException {
// TODO Auto-generated method stub
return false;
}
+ @Override
public void statusBarVisibilityChanged(int arg0) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public void stopAppFreezingScreen(IBinder arg0, boolean arg1) throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public boolean stopViewServer() throws RemoteException {
// TODO Auto-generated method stub
return false;
}
+ @Override
public void thawRotation() throws RemoteException {
// TODO Auto-generated method stub
}
+ @Override
public Configuration updateOrientationFromAppTokens(Configuration arg0, IBinder arg1)
throws RemoteException {
// TODO Auto-generated method stub
return null;
}
+ @Override
public int watchRotation(IRotationWatcher arg0) throws RemoteException {
// TODO Auto-generated method stub
return 0;
}
+ @Override
public void waitForWindowDrawn(IBinder token, IRemoteCallback callback) {
// TODO Auto-generated method stub
}
-
+
+ @Override
public IBinder asBinder() {
// TODO Auto-generated method stub
return null;
}
+ @Override
public int getPreferredOptionsPanelGravity() throws RemoteException {
return Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
}
+ @Override
public void dismissKeyguard() {
}
+ @Override
public boolean hasNavigationBar() {
return false; // should this return something else?
}
+ @Override
public void lockNow() {
// TODO Auto-generated method stub
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
index a640a91..d3721ed 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
@@ -26,7 +26,6 @@
import android.view.IWindow;
import android.view.IWindowSession;
import android.view.InputChannel;
-import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceView;
import android.view.WindowManager.LayoutParams;
@@ -37,6 +36,7 @@
*/
public final class BridgeWindowSession implements IWindowSession {
+ @Override
public int add(IWindow arg0, int seq, LayoutParams arg1, int arg2, Rect arg3,
InputChannel outInputchannel)
throws RemoteException {
@@ -44,40 +44,30 @@
return 0;
}
+ @Override
public int addWithoutInputChannel(IWindow arg0, int seq, LayoutParams arg1, int arg2, Rect arg3)
throws RemoteException {
// pass for now.
return 0;
}
+ @Override
public void finishDrawing(IWindow arg0) throws RemoteException {
// pass for now.
}
- public void finishKey(IWindow arg0) throws RemoteException {
- // pass for now.
- }
-
+ @Override
public boolean getInTouchMode() throws RemoteException {
// pass for now.
return false;
}
+ @Override
public boolean performHapticFeedback(IWindow window, int effectId, boolean always) {
// pass for now.
return false;
}
-
- public MotionEvent getPendingPointerMove(IWindow arg0) throws RemoteException {
- // pass for now.
- return null;
- }
-
- public MotionEvent getPendingTrackballMove(IWindow arg0) throws RemoteException {
- // pass for now.
- return null;
- }
-
+ @Override
public int relayout(IWindow arg0, int seq, LayoutParams arg1, int arg2, int arg3, int arg4,
int arg4_5, Rect arg5, Rect arg6, Rect arg7, Configuration arg7b, Surface arg8)
throws RemoteException {
@@ -85,35 +75,43 @@
return 0;
}
+ @Override
public void performDeferredDestroy(IWindow window) {
// pass for now.
}
+ @Override
public boolean outOfMemory(IWindow window) throws RemoteException {
return false;
}
+ @Override
public void getDisplayFrame(IWindow window, Rect outDisplayFrame) {
// pass for now.
}
+ @Override
public void remove(IWindow arg0) throws RemoteException {
// pass for now.
}
+ @Override
public void setInTouchMode(boolean arg0) throws RemoteException {
// pass for now.
}
+ @Override
public void setTransparentRegion(IWindow arg0, Region arg1) throws RemoteException {
// pass for now.
}
+ @Override
public void setInsets(IWindow window, int touchable, Rect contentInsets,
Rect visibleInsets, Region touchableRegion) {
// pass for now.
}
+ @Override
public IBinder prepareDrag(IWindow window, int flags,
int thumbnailWidth, int thumbnailHeight, Surface outSurface)
throws RemoteException {
@@ -121,6 +119,7 @@
return null;
}
+ @Override
public boolean performDrag(IWindow window, IBinder dragToken,
float touchX, float touchY, float thumbCenterX, float thumbCenterY,
ClipData data)
@@ -129,49 +128,47 @@
return false;
}
+ @Override
public void reportDropResult(IWindow window, boolean consumed) throws RemoteException {
// pass for now
}
+ @Override
public void dragRecipientEntered(IWindow window) throws RemoteException {
// pass for now
}
+ @Override
public void dragRecipientExited(IWindow window) throws RemoteException {
// pass for now
}
+ @Override
public void setWallpaperPosition(IBinder window, float x, float y,
float xStep, float yStep) {
// pass for now.
}
+ @Override
public void wallpaperOffsetsComplete(IBinder window) {
// pass for now.
}
+ @Override
public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y,
int z, Bundle extras, boolean sync) {
// pass for now.
return null;
}
+ @Override
public void wallpaperCommandComplete(IBinder window, Bundle result) {
// pass for now.
}
- public void closeSystemDialogs(String reason) {
- // pass for now.
- }
-
+ @Override
public IBinder asBinder() {
// pass for now.
return null;
}
-
- public IBinder prepareDrag(IWindow arg0, boolean arg1, int arg2, int arg3, Surface arg4)
- throws RemoteException {
- // TODO Auto-generated method stub
- return null;
- }
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java
index f8ed4f7..ac8712e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java
@@ -95,6 +95,7 @@
// ------- XmlResourceParser implementation
+ @Override
public void setFeature(String name, boolean state)
throws XmlPullParserException {
if (FEATURE_PROCESS_NAMESPACES.equals(name) && state) {
@@ -106,6 +107,7 @@
throw new XmlPullParserException("Unsupported feature: " + name);
}
+ @Override
public boolean getFeature(String name) {
if (FEATURE_PROCESS_NAMESPACES.equals(name)) {
return true;
@@ -116,82 +118,101 @@
return false;
}
+ @Override
public void setProperty(String name, Object value) throws XmlPullParserException {
throw new XmlPullParserException("setProperty() not supported");
}
+ @Override
public Object getProperty(String name) {
return null;
}
+ @Override
public void setInput(Reader in) throws XmlPullParserException {
mParser.setInput(in);
}
+ @Override
public void setInput(InputStream inputStream, String inputEncoding)
throws XmlPullParserException {
mParser.setInput(inputStream, inputEncoding);
}
+ @Override
public void defineEntityReplacementText(String entityName,
String replacementText) throws XmlPullParserException {
throw new XmlPullParserException(
"defineEntityReplacementText() not supported");
}
+ @Override
public String getNamespacePrefix(int pos) throws XmlPullParserException {
throw new XmlPullParserException("getNamespacePrefix() not supported");
}
+ @Override
public String getInputEncoding() {
return null;
}
+ @Override
public String getNamespace(String prefix) {
throw new RuntimeException("getNamespace() not supported");
}
+ @Override
public int getNamespaceCount(int depth) throws XmlPullParserException {
throw new XmlPullParserException("getNamespaceCount() not supported");
}
+ @Override
public String getPositionDescription() {
return "Binary XML file line #" + getLineNumber();
}
+ @Override
public String getNamespaceUri(int pos) throws XmlPullParserException {
throw new XmlPullParserException("getNamespaceUri() not supported");
}
+ @Override
public int getColumnNumber() {
return -1;
}
+ @Override
public int getDepth() {
return mParser.getDepth();
}
+ @Override
public String getText() {
return mParser.getText();
}
+ @Override
public int getLineNumber() {
return mParser.getLineNumber();
}
+ @Override
public int getEventType() {
return mEventType;
}
+ @Override
public boolean isWhitespace() throws XmlPullParserException {
// Original comment: whitespace was stripped by aapt.
return mParser.isWhitespace();
}
+ @Override
public String getPrefix() {
throw new RuntimeException("getPrefix not supported");
}
+ @Override
public char[] getTextCharacters(int[] holderForStartAndLength) {
String txt = getText();
char[] chars = null;
@@ -204,55 +225,68 @@
return chars;
}
+ @Override
public String getNamespace() {
return mParser.getNamespace();
}
+ @Override
public String getName() {
return mParser.getName();
}
+ @Override
public String getAttributeNamespace(int index) {
return mParser.getAttributeNamespace(index);
}
+ @Override
public String getAttributeName(int index) {
return mParser.getAttributeName(index);
}
+ @Override
public String getAttributePrefix(int index) {
throw new RuntimeException("getAttributePrefix not supported");
}
+ @Override
public boolean isEmptyElementTag() {
// XXX Need to detect this.
return false;
}
+ @Override
public int getAttributeCount() {
return mParser.getAttributeCount();
}
+ @Override
public String getAttributeValue(int index) {
return mParser.getAttributeValue(index);
}
+ @Override
public String getAttributeType(int index) {
return "CDATA";
}
+ @Override
public boolean isAttributeDefault(int index) {
return false;
}
+ @Override
public int nextToken() throws XmlPullParserException, IOException {
return next();
}
+ @Override
public String getAttributeValue(String namespace, String name) {
return mParser.getAttributeValue(namespace, name);
}
+ @Override
public int next() throws XmlPullParserException, IOException {
if (!mStarted) {
mStarted = true;
@@ -313,6 +347,7 @@
return "????";
}
+ @Override
public void require(int type, String namespace, String name)
throws XmlPullParserException {
if (type != getEventType()
@@ -322,6 +357,7 @@
+ getPositionDescription());
}
+ @Override
public String nextText() throws XmlPullParserException, IOException {
if (getEventType() != START_TAG) {
throw new XmlPullParserException(getPositionDescription()
@@ -348,6 +384,7 @@
}
}
+ @Override
public int nextTag() throws XmlPullParserException, IOException {
int eventType = next();
if (eventType == TEXT && isWhitespace()) { // skip whitespace
@@ -363,76 +400,94 @@
// AttributeSet implementation
+ @Override
public void close() {
// pass
}
+ @Override
public boolean getAttributeBooleanValue(int index, boolean defaultValue) {
return mAttrib.getAttributeBooleanValue(index, defaultValue);
}
+ @Override
public boolean getAttributeBooleanValue(String namespace, String attribute,
boolean defaultValue) {
return mAttrib.getAttributeBooleanValue(namespace, attribute, defaultValue);
}
+ @Override
public float getAttributeFloatValue(int index, float defaultValue) {
return mAttrib.getAttributeFloatValue(index, defaultValue);
}
+ @Override
public float getAttributeFloatValue(String namespace, String attribute, float defaultValue) {
return mAttrib.getAttributeFloatValue(namespace, attribute, defaultValue);
}
+ @Override
public int getAttributeIntValue(int index, int defaultValue) {
return mAttrib.getAttributeIntValue(index, defaultValue);
}
+ @Override
public int getAttributeIntValue(String namespace, String attribute, int defaultValue) {
return mAttrib.getAttributeIntValue(namespace, attribute, defaultValue);
}
+ @Override
public int getAttributeListValue(int index, String[] options, int defaultValue) {
return mAttrib.getAttributeListValue(index, options, defaultValue);
}
+ @Override
public int getAttributeListValue(String namespace, String attribute,
String[] options, int defaultValue) {
return mAttrib.getAttributeListValue(namespace, attribute, options, defaultValue);
}
+ @Override
public int getAttributeNameResource(int index) {
return mAttrib.getAttributeNameResource(index);
}
+ @Override
public int getAttributeResourceValue(int index, int defaultValue) {
return mAttrib.getAttributeResourceValue(index, defaultValue);
}
+ @Override
public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) {
return mAttrib.getAttributeResourceValue(namespace, attribute, defaultValue);
}
+ @Override
public int getAttributeUnsignedIntValue(int index, int defaultValue) {
return mAttrib.getAttributeUnsignedIntValue(index, defaultValue);
}
+ @Override
public int getAttributeUnsignedIntValue(String namespace, String attribute, int defaultValue) {
return mAttrib.getAttributeUnsignedIntValue(namespace, attribute, defaultValue);
}
+ @Override
public String getClassAttribute() {
return mAttrib.getClassAttribute();
}
+ @Override
public String getIdAttribute() {
return mAttrib.getIdAttribute();
}
+ @Override
public int getIdAttributeResourceValue(int defaultValue) {
return mAttrib.getIdAttributeResourceValue(defaultValue);
}
+ @Override
public int getStyleAttribute() {
return mAttrib.getStyleAttribute();
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index d5400d7..6840f46 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -33,10 +33,10 @@
import com.android.ide.common.rendering.api.ResourceReference;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.rendering.api.Result;
-import com.android.ide.common.rendering.api.SessionParams;
-import com.android.ide.common.rendering.api.ViewInfo;
import com.android.ide.common.rendering.api.Result.Status;
+import com.android.ide.common.rendering.api.SessionParams;
import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
+import com.android.ide.common.rendering.api.ViewInfo;
import com.android.internal.util.XmlUtils;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext;
@@ -69,8 +69,8 @@
import android.view.AttachInfo_Accessor;
import android.view.BridgeInflater;
import android.view.View;
-import android.view.ViewGroup;
import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.AbsListView;
@@ -82,8 +82,8 @@
import android.widget.ListView;
import android.widget.QuickContactBadge;
import android.widget.TabHost;
-import android.widget.TabWidget;
import android.widget.TabHost.TabSpec;
+import android.widget.TabWidget;
import java.awt.AlphaComposite;
import java.awt.Color;
@@ -835,6 +835,7 @@
previousTransition.addTransitionListener(new TransitionListener() {
private int mChangeDisappearingCount = 0;
+ @Override
public void startTransition(LayoutTransition transition, ViewGroup container,
View view, int transitionType) {
if (transitionType == LayoutTransition.CHANGE_DISAPPEARING) {
@@ -842,6 +843,7 @@
}
}
+ @Override
public void endTransition(LayoutTransition transition, ViewGroup container,
View view, int transitionType) {
if (transitionType == LayoutTransition.CHANGE_DISAPPEARING) {
@@ -1227,6 +1229,7 @@
TabSpec spec = tabHost.newTabSpec("tag").setIndicator("Tab Label",
tabHost.getResources().getDrawable(android.R.drawable.ic_menu_info_details))
.setContent(new TabHost.TabContentFactory() {
+ @Override
public View createTabContent(String tag) {
return new LinearLayout(getContext());
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java
index c9bb424..22570b9 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java
@@ -74,38 +74,46 @@
}
}
+ @Override
public boolean isEnabled(int position) {
return true;
}
+ @Override
public int getCount() {
return mItems.size();
}
+ @Override
public Object getItem(int position) {
return mItems.get(position);
}
+ @Override
public long getItemId(int position) {
return position;
}
+ @Override
public int getItemViewType(int position) {
return mItems.get(position).getType();
}
+ @Override
public View getView(int position, View convertView, ViewGroup parent) {
// we don't care about recycling here because we never scroll.
AdapterItem item = mItems.get(position);
return getView(item, null /*parentGroup*/, convertView, parent);
}
+ @Override
public int getViewTypeCount() {
return mTypes.size();
}
// ---- SpinnerAdapter
+ @Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
// pass
return null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java
index 2c492e3..199e040 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java
@@ -99,23 +99,28 @@
// ---- ExpandableListAdapter
+ @Override
public int getGroupCount() {
return mItems.size();
}
+ @Override
public int getChildrenCount(int groupPosition) {
AdapterItem item = mItems.get(groupPosition);
return item.getChildren().size();
}
+ @Override
public Object getGroup(int groupPosition) {
return mItems.get(groupPosition);
}
+ @Override
public Object getChild(int groupPosition, int childPosition) {
return getChildItem(groupPosition, childPosition);
}
+ @Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
ViewGroup parent) {
// we don't care about recycling here because we never scroll.
@@ -123,6 +128,7 @@
return getView(item, null /*parentItem*/, convertView, parent);
}
+ @Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
View convertView, ViewGroup parent) {
// we don't care about recycling here because we never scroll.
@@ -131,48 +137,59 @@
return getView(item, parentItem, convertView, parent);
}
+ @Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
+ @Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
+ @Override
public long getCombinedGroupId(long groupId) {
return groupId << 16 | 0x0000FFFF;
}
+ @Override
public long getCombinedChildId(long groupId, long childId) {
return groupId << 16 | childId;
}
+ @Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
+ @Override
public void onGroupCollapsed(int groupPosition) {
// pass
}
+ @Override
public void onGroupExpanded(int groupPosition) {
// pass
}
// ---- HeterogeneousExpandableList
+ @Override
public int getChildType(int groupPosition, int childPosition) {
return getChildItem(groupPosition, childPosition).getType();
}
+ @Override
public int getChildTypeCount() {
return mChildrenTypes.size();
}
+ @Override
public int getGroupType(int groupPosition) {
return mItems.get(groupPosition).getType();
}
+ @Override
public int getGroupTypeCount() {
return mGroupTypes.size();
}
diff --git a/tools/layoutlib/create/.settings/README.txt b/tools/layoutlib/create/.settings/README.txt
new file mode 100644
index 0000000..9120b20
--- /dev/null
+++ b/tools/layoutlib/create/.settings/README.txt
@@ -0,0 +1,2 @@
+Copy this in eclipse project as a .settings folder at the root.
+This ensure proper compilation compliance and warning/error levels.
\ No newline at end of file
diff --git a/tools/layoutlib/create/.settings/org.eclipse.jdt.core.prefs b/tools/layoutlib/create/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..5381a0e
--- /dev/null
+++ b/tools/layoutlib/create/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,93 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.annotation.nonnull=com.android.annotations.NonNull
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=com.android.annotations.NonNullByDefault
+org.eclipse.jdt.core.compiler.annotation.nonnullisdefault=disabled
+org.eclipse.jdt.core.compiler.annotation.nullable=com.android.annotations.Nullable
+org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=enabled
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.nullSpecInsufficientInfo=warning
+org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=warning
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.unclosedCloseable=error
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 70c8a00..4b33474 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -27,6 +27,7 @@
* Returns the list of class from layoutlib_create to inject in layoutlib.
* The list can be empty but must not be null.
*/
+ @Override
public Class<?>[] getInjectedClasses() {
return INJECTED_CLASSES;
}
@@ -35,6 +36,7 @@
* Returns the list of methods to rewrite as delegates.
* The list can be empty but must not be null.
*/
+ @Override
public String[] getDelegateMethods() {
return DELEGATE_METHODS;
}
@@ -43,6 +45,7 @@
* Returns the list of classes on which to delegate all native methods.
* The list can be empty but must not be null.
*/
+ @Override
public String[] getDelegateClassNatives() {
return DELEGATE_CLASS_NATIVES;
}
@@ -54,6 +57,7 @@
* <p/>
* This usage is deprecated. Please use method 'delegates' instead.
*/
+ @Override
public String[] getOverriddenMethods() {
return OVERRIDDEN_METHODS;
}
@@ -63,6 +67,7 @@
* of class to replace followed by the new FQCN.
* The list can be empty but must not be null.
*/
+ @Override
public String[] getRenamedClasses() {
return RENAMED_CLASSES;
}
@@ -74,6 +79,7 @@
* the methods to delete.
* The list can be empty but must not be null.
*/
+ @Override
public String[] getDeleteReturns() {
return DELETE_RETURNS;
}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodAdapter.java
index 627ea17..7d1e4cf 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodAdapter.java
@@ -28,13 +28,14 @@
* A stub method is being invoked.
* <p/>
* Known limitation: caller arguments are not available.
- *
+ *
* @param signature The signature of the method being invoked, composed of the
* binary class name followed by the method descriptor (aka argument
* types). Example: "com/foo/MyClass/InnerClass/printInt(I)V".
* @param isNative True if the method was a native method.
* @param caller The calling object. Null for static methods, "this" for instance methods.
*/
+ @Override
public void onInvokeV(String signature, boolean isNative, Object caller) {
}
@@ -43,6 +44,7 @@
* @see #onInvokeV(String, boolean, Object)
* @return an integer, or a boolean, or a short or a byte.
*/
+ @Override
public int onInvokeI(String signature, boolean isNative, Object caller) {
onInvokeV(signature, isNative, caller);
return 0;
@@ -53,6 +55,7 @@
* @see #onInvokeV(String, boolean, Object)
* @return a long.
*/
+ @Override
public long onInvokeL(String signature, boolean isNative, Object caller) {
onInvokeV(signature, isNative, caller);
return 0;
@@ -63,6 +66,7 @@
* @see #onInvokeV(String, boolean, Object)
* @return a float.
*/
+ @Override
public float onInvokeF(String signature, boolean isNative, Object caller) {
onInvokeV(signature, isNative, caller);
return 0;
@@ -73,6 +77,7 @@
* @see #onInvokeV(String, boolean, Object)
* @return a double.
*/
+ @Override
public double onInvokeD(String signature, boolean isNative, Object caller) {
onInvokeV(signature, isNative, caller);
return 0;
@@ -83,6 +88,7 @@
* @see #onInvokeV(String, boolean, Object)
* @return an object.
*/
+ @Override
public Object onInvokeA(String signature, boolean isNative, Object caller) {
onInvokeV(signature, isNative, caller);
return null;
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
index f4ff389..7b76a5b 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
@@ -65,24 +65,29 @@
public void testClassRenaming() throws IOException, LogAbortException {
ICreateInfo ci = new ICreateInfo() {
+ @Override
public Class<?>[] getInjectedClasses() {
// classes to inject in the final JAR
return new Class<?>[0];
}
+ @Override
public String[] getDelegateMethods() {
return new String[0];
}
+ @Override
public String[] getDelegateClassNatives() {
return new String[0];
}
+ @Override
public String[] getOverriddenMethods() {
// methods to force override
return new String[0];
}
+ @Override
public String[] getRenamedClasses() {
// classes to rename (so that we can replace them)
return new String[] {
@@ -91,6 +96,7 @@
};
}
+ @Override
public String[] getDeleteReturns() {
// methods deleted from their return type.
return new String[0];