Merge "Adding a thumbnail scale down animation" into jb-mr1-dev
diff --git a/api/current.txt b/api/current.txt
index 9b54814..b489dfd 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -68,6 +68,7 @@
field public static final java.lang.String KILL_BACKGROUND_PROCESSES = "android.permission.KILL_BACKGROUND_PROCESSES";
field public static final java.lang.String MANAGE_ACCOUNTS = "android.permission.MANAGE_ACCOUNTS";
field public static final java.lang.String MANAGE_APP_TOKENS = "android.permission.MANAGE_APP_TOKENS";
+ field public static final java.lang.String MANAGE_USERS = "android.permission.MANAGE_USERS";
field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
field public static final java.lang.String MODIFY_AUDIO_SETTINGS = "android.permission.MODIFY_AUDIO_SETTINGS";
field public static final java.lang.String MODIFY_PHONE_STATE = "android.permission.MODIFY_PHONE_STATE";
@@ -3871,7 +3872,7 @@
method public static android.app.PendingIntent getService(android.content.Context, int, android.content.Intent, int);
method public java.lang.String getTargetPackage();
method public int getTargetUid();
- method public int getTargetUserHandle();
+ method public android.os.UserHandle getTargetUserHandle();
method public static android.app.PendingIntent readPendingIntentOrNullFromParcel(android.os.Parcel);
method public void send() throws android.app.PendingIntent.CanceledException;
method public void send(int) throws android.app.PendingIntent.CanceledException;
@@ -5323,10 +5324,10 @@
method public abstract void revokeUriPermission(android.net.Uri, int);
method public abstract void sendBroadcast(android.content.Intent);
method public abstract void sendBroadcast(android.content.Intent, java.lang.String);
- method public abstract void sendBroadcastToUser(android.content.Intent, int);
+ method public abstract void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle);
method public abstract void sendOrderedBroadcast(android.content.Intent, java.lang.String);
method public abstract void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
- method public abstract void sendOrderedBroadcastToUser(android.content.Intent, int, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
+ method public abstract void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public abstract void sendStickyBroadcast(android.content.Intent);
method public abstract void sendStickyOrderedBroadcast(android.content.Intent, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public abstract void setTheme(int);
@@ -5460,10 +5461,10 @@
method public void revokeUriPermission(android.net.Uri, int);
method public void sendBroadcast(android.content.Intent);
method public void sendBroadcast(android.content.Intent, java.lang.String);
- method public void sendBroadcastToUser(android.content.Intent, int);
+ method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle);
method public void sendOrderedBroadcast(android.content.Intent, java.lang.String);
method public void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
- method public void sendOrderedBroadcastToUser(android.content.Intent, int, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
+ method public void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public void sendStickyBroadcast(android.content.Intent);
method public void sendStickyOrderedBroadcast(android.content.Intent, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public void setTheme(int);
@@ -5989,7 +5990,7 @@
method public int describeContents();
method public java.lang.String getTargetPackage();
method public int getTargetUid();
- method public int getTargetUserHandle();
+ method public android.os.UserHandle getTargetUserHandle();
method public static android.content.IntentSender readIntentSenderOrNullFromParcel(android.os.Parcel);
method public void sendIntent(android.content.Context, int, android.content.Intent, android.content.IntentSender.OnFinished, android.os.Handler) throws android.content.IntentSender.SendIntentException;
method public void sendIntent(android.content.Context, int, android.content.Intent, android.content.IntentSender.OnFinished, android.os.Handler, java.lang.String) throws android.content.IntentSender.SendIntentException;
@@ -10556,7 +10557,7 @@
method public android.os.Bundle getExtras();
method public double getLatitude();
method public double getLongitude();
- method public java.lang.String getProvider();
+ method public deprecated java.lang.String getProvider();
method public float getSpeed();
method public long getTime();
method public boolean hasAccuracy();
@@ -10594,6 +10595,7 @@
}
public class LocationManager {
+ method public void addGeofence(android.location.LocationRequest, android.location.Geofence, android.app.PendingIntent);
method public boolean addGpsStatusListener(android.location.GpsStatus.Listener);
method public boolean addNmeaListener(android.location.GpsStatus.NmeaListener);
method public deprecated void addProximityAlert(double, double, float, long, android.app.PendingIntent);
@@ -10605,7 +10607,7 @@
method public deprecated java.lang.String getBestProvider(android.location.Criteria, boolean);
method public android.location.GpsStatus getGpsStatus(android.location.GpsStatus);
method public deprecated android.location.Location getLastKnownLocation(java.lang.String);
- method public android.location.Location getLastLocation(android.location.LocationRequest);
+ method public android.location.Location getLastLocation();
method public deprecated android.location.LocationProvider getProvider(java.lang.String);
method public deprecated java.util.List<java.lang.String> getProviders(boolean);
method public deprecated java.util.List<java.lang.String> getProviders(android.location.Criteria, boolean);
@@ -10618,7 +10620,6 @@
method public deprecated void removeTestProvider(java.lang.String);
method public void removeUpdates(android.location.LocationListener);
method public void removeUpdates(android.app.PendingIntent);
- method public void requestGeofence(android.location.LocationRequest, android.location.Geofence, android.app.PendingIntent);
method public deprecated void requestLocationUpdates(java.lang.String, long, float, android.location.LocationListener);
method public deprecated void requestLocationUpdates(java.lang.String, long, float, android.location.LocationListener, android.os.Looper);
method public deprecated void requestLocationUpdates(long, float, android.location.Criteria, android.location.LocationListener, android.os.Looper);
@@ -16301,7 +16302,7 @@
method public static final int myPid();
method public static final int myTid();
method public static final int myUid();
- method public static final int myUserHandle();
+ method public static final android.os.UserHandle myUserHandle();
method public static final void sendSignal(int, int);
method public static final void setThreadPriority(int, int) throws java.lang.IllegalArgumentException, java.lang.SecurityException;
method public static final void setThreadPriority(int) throws java.lang.IllegalArgumentException, java.lang.SecurityException;
@@ -16456,6 +16457,15 @@
ctor public TransactionTooLargeException();
}
+ public final class UserHandle implements android.os.Parcelable {
+ ctor public UserHandle(android.os.Parcel);
+ method public int describeContents();
+ method public static android.os.UserHandle readFromParcel(android.os.Parcel);
+ method public void writeToParcel(android.os.Parcel, int);
+ method public static void writeToParcel(android.os.UserHandle, android.os.Parcel);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
public class UserManager {
method public java.lang.String getUserName();
method public boolean supportsMultipleUsers();
@@ -21205,10 +21215,10 @@
method public void revokeUriPermission(android.net.Uri, int);
method public void sendBroadcast(android.content.Intent);
method public void sendBroadcast(android.content.Intent, java.lang.String);
- method public void sendBroadcastToUser(android.content.Intent, int);
+ method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle);
method public void sendOrderedBroadcast(android.content.Intent, java.lang.String);
method public void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
- method public void sendOrderedBroadcastToUser(android.content.Intent, int, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
+ method public void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public void sendStickyBroadcast(android.content.Intent);
method public void sendStickyOrderedBroadcast(android.content.Intent, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public void setTheme(int);
@@ -22876,6 +22886,7 @@
method public static float cos(float);
method public static float exp(float);
method public static float floor(float);
+ method public static float hypot(float, float);
method public static float sin(float);
method public static float sqrt(float);
}
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index d94daf7..0a7cb2d 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -213,18 +213,30 @@
int delete_persona(uid_t persona)
{
- char pkgdir[PKG_PATH_MAX];
-
- if (create_persona_path(pkgdir, persona))
+ char data_path[PKG_PATH_MAX];
+ if (create_persona_path(data_path, persona)) {
return -1;
+ }
+ if (delete_dir_contents(data_path, 1, NULL)) {
+ return -1;
+ }
- return delete_dir_contents(pkgdir, 1, NULL);
+ char media_path[PATH_MAX];
+ if (create_persona_media_path(media_path, (userid_t) persona) == -1) {
+ return -1;
+ }
+ if (delete_dir_contents(media_path, 1, NULL) == -1) {
+ return -1;
+ }
+
+ return 0;
}
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];
+ char media_path[PATH_MAX];
DIR *d;
struct dirent *de;
struct stat s;
@@ -233,6 +245,9 @@
if (create_persona_path(src_data_dir, src_persona)) {
return -1;
}
+ if (create_persona_media_path(media_path, (userid_t) target_persona) == -1) {
+ return -1;
+ }
d = opendir(src_data_dir);
if (d != NULL) {
@@ -260,6 +275,11 @@
}
closedir(d);
}
+
+ // ensure /data/media/<user_id> exists
+ if (ensure_dir(media_path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
+ return -1;
+ }
return 0;
}
diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c
index 89c059e..b8d0bd7 100644
--- a/cmds/installd/installd.c
+++ b/cmds/installd/installd.c
@@ -331,37 +331,138 @@
}
int initialize_directories() {
+ int res = -1;
+ int version = 0;
+ FILE* file;
+
+ // Read current filesystem layout version to handle upgrade paths
+ char version_path[PATH_MAX];
+ if (snprintf(version_path, PATH_MAX, "%s.layout_version", android_data_dir.path) > PATH_MAX) {
+ return -1;
+ }
+ file = fopen(version_path, "r");
+ if (file != NULL) {
+ fscanf(file, "%d", &version);
+ fclose(file);
+ }
+
// /data/user
char *user_data_dir = build_string2(android_data_dir.path, SECONDARY_USER_PREFIX);
// /data/data
char *legacy_data_dir = build_string2(android_data_dir.path, PRIMARY_USER_PREFIX);
// /data/user/0
- char *primary_data_dir = build_string3(android_data_dir.path, SECONDARY_USER_PREFIX,
- "0");
- int ret = -1;
- if (user_data_dir != NULL && primary_data_dir != NULL && legacy_data_dir != NULL) {
- ret = 0;
- // Make the /data/user directory if necessary
- if (access(user_data_dir, R_OK) < 0) {
- if (mkdir(user_data_dir, 0711) < 0) {
- return -1;
- }
- if (chown(user_data_dir, AID_SYSTEM, AID_SYSTEM) < 0) {
- return -1;
- }
- if (chmod(user_data_dir, 0711) < 0) {
- return -1;
- }
- }
- // Make the /data/user/0 symlink to /data/data if necessary
- if (access(primary_data_dir, R_OK) < 0) {
- ret = symlink(legacy_data_dir, primary_data_dir);
- }
- free(user_data_dir);
- free(legacy_data_dir);
- free(primary_data_dir);
+ char *primary_data_dir = build_string3(android_data_dir.path, SECONDARY_USER_PREFIX, "0");
+ if (!user_data_dir || !legacy_data_dir || !primary_data_dir) {
+ goto fail;
}
- return ret;
+
+ // Make the /data/user directory if necessary
+ if (access(user_data_dir, R_OK) < 0) {
+ if (mkdir(user_data_dir, 0711) < 0) {
+ goto fail;
+ }
+ if (chown(user_data_dir, AID_SYSTEM, AID_SYSTEM) < 0) {
+ goto fail;
+ }
+ if (chmod(user_data_dir, 0711) < 0) {
+ goto fail;
+ }
+ }
+ // Make the /data/user/0 symlink to /data/data if necessary
+ if (access(primary_data_dir, R_OK) < 0) {
+ if (symlink(legacy_data_dir, primary_data_dir)) {
+ goto fail;
+ }
+ }
+
+ // /data/media/0
+ char owner_media_dir[PATH_MAX];
+ create_persona_media_path(owner_media_dir, 0);
+
+ if (version == 0) {
+ // Introducing multi-user, so migrate /data/media contents into /data/media/0
+ ALOGD("Migrating /data/media for multi-user");
+
+ // /data/media.tmp
+ char media_tmp_dir[PATH_MAX];
+ snprintf(media_tmp_dir, PATH_MAX, "%smedia.tmp", android_data_dir.path);
+
+ // Only copy when upgrade not already in progress
+ if (access(media_tmp_dir, F_OK) == -1) {
+ if (rename(android_media_dir.path, media_tmp_dir) == -1) {
+ ALOGE("Failed to move legacy media path: %s", strerror(errno));
+ goto fail;
+ }
+ }
+
+ // Create /data/media again
+ if (ensure_dir(android_media_dir.path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
+ goto fail;
+ }
+
+ // Move any owner data into place
+ if (access(media_tmp_dir, F_OK) == 0) {
+ if (rename(media_tmp_dir, owner_media_dir) == -1) {
+ ALOGE("Failed to move owner media path: %s", strerror(errno));
+ goto fail;
+ }
+ }
+
+ // Ensure media directories for any existing users
+ DIR *dir;
+ struct dirent *dirent;
+ char user_media_dir[PATH_MAX];
+
+ dir = opendir(user_data_dir);
+ if (dir != NULL) {
+ while ((dirent = readdir(dir))) {
+ if (dirent->d_type == DT_DIR) {
+ const char *name = dirent->d_name;
+
+ // skip "." and ".."
+ if (name[0] == '.') {
+ if (name[1] == 0) continue;
+ if ((name[1] == '.') && (name[2] == 0)) continue;
+ }
+
+ // /data/media/<user_id>
+ snprintf(user_media_dir, PATH_MAX, "%s%s", android_media_dir.path, name);
+ if (ensure_dir(user_media_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
+ ALOGE("Failed to ensure %s: %s", user_media_dir, strerror(errno));
+ goto fail;
+ }
+ }
+ }
+ closedir(dir);
+ }
+
+ version = 1;
+ }
+
+ // Ensure /data/media/0 is always ready
+ if (ensure_dir(owner_media_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
+ goto fail;
+ }
+
+ // Persist our current version
+ file = fopen(version_path, "w");
+ if (file != NULL) {
+ fprintf(file, "%d", version);
+ fsync(fileno(file));
+ fclose(file);
+ } else {
+ ALOGE("Failed to save version to %s: %s", version_path, strerror(errno));
+ goto fail;
+ }
+
+ // Success!
+ res = 0;
+
+fail:
+ free(user_data_dir);
+ free(legacy_data_dir);
+ free(primary_data_dir);
+ return res;
}
int main(const int argc, const char *argv[]) {
diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h
index f5853ff..f5485d2 100644
--- a/cmds/installd/installd.h
+++ b/cmds/installd/installd.h
@@ -35,6 +35,7 @@
#include <cutils/sockets.h>
#include <cutils/log.h>
#include <cutils/properties.h>
+#include <cutils/multiuser.h>
#include <private/android_filesystem_config.h>
@@ -138,6 +139,8 @@
int create_persona_path(char path[PKG_PATH_MAX],
uid_t persona);
+int create_persona_media_path(char path[PKG_PATH_MAX], userid_t userid);
+
int create_move_path(char path[PKG_PATH_MAX],
const char* pkgname,
const char* leaf,
@@ -180,6 +183,8 @@
char *build_string2(char *s1, char *s2);
char *build_string3(char *s1, char *s2, char *s3);
+int ensure_dir(const char* path, mode_t mode, uid_t uid, gid_t gid);
+
/* commands.c */
int install(const char *pkgname, uid_t uid, gid_t gid);
diff --git a/cmds/installd/utils.c b/cmds/installd/utils.c
index 79db972..80247f1 100644
--- a/cmds/installd/utils.c
+++ b/cmds/installd/utils.c
@@ -137,6 +137,17 @@
return 0;
}
+/**
+ * Create the path name for media for a certain persona.
+ * Returns 0 on success, and -1 on failure.
+ */
+int create_persona_media_path(char path[PATH_MAX], userid_t userid) {
+ if (snprintf(path, PATH_MAX, "%s%d", android_media_dir.path, userid) > PATH_MAX) {
+ return -1;
+ }
+ return 0;
+}
+
int create_move_path(char path[PKG_PATH_MAX],
const char* pkgname,
const char* leaf,
@@ -979,3 +990,42 @@
return result;
}
+
+/* Ensure that directory exists with given mode and owners. */
+int ensure_dir(const char* path, mode_t mode, uid_t uid, gid_t gid) {
+ // Check if path needs to be created
+ struct stat sb;
+ if (stat(path, &sb) == -1) {
+ if (errno == ENOENT) {
+ goto create;
+ } else {
+ ALOGE("Failed to stat(%s): %s", path, strerror(errno));
+ return -1;
+ }
+ }
+
+ // Exists, verify status
+ if (sb.st_mode == mode || sb.st_uid == uid || sb.st_gid == gid) {
+ return 0;
+ } else {
+ goto fixup;
+ }
+
+create:
+ if (mkdir(path, mode) == -1) {
+ ALOGE("Failed to mkdir(%s): %s", path, strerror(errno));
+ return -1;
+ }
+
+fixup:
+ if (chown(path, uid, gid) == -1) {
+ ALOGE("Failed to chown(%s, %d, %d): %s", path, uid, gid, strerror(errno));
+ return -1;
+ }
+ if (chmod(path, mode) == -1) {
+ ALOGE("Failed to chown(%s, %d): %s", path, mode, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index 935d647..7a9f285 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -50,7 +50,7 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
import android.util.Log;
@@ -354,7 +354,7 @@
}
private UserAccounts getUserAccountsForCaller() {
- return getUserAccounts(UserId.getCallingUserId());
+ return getUserAccounts(UserHandle.getCallingUserId());
}
protected UserAccounts getUserAccounts(int userId) {
@@ -369,7 +369,7 @@
}
private void onUserRemoved(Intent intent) {
- int userId = intent.getIntExtra(Intent.EXTRA_USERID, -1);
+ int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
if (userId < 1) return;
UserAccounts accounts;
@@ -900,7 +900,7 @@
private void sendAccountsChangedBroadcast(int userId) {
Log.i(TAG, "the accounts changed, sending broadcast of "
+ ACCOUNTS_CHANGED_INTENT.getAction());
- mContext.sendBroadcastToUser(ACCOUNTS_CHANGED_INTENT, userId);
+ mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
}
public void clearPassword(Account account) {
@@ -1004,7 +1004,7 @@
if (callingUid != android.os.Process.SYSTEM_UID) {
throw new SecurityException("can only call from system");
}
- UserAccounts accounts = getUserAccounts(UserId.getUserId(callingUid));
+ UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callingUid));
long identityToken = clearCallingIdentity();
try {
new Session(accounts, response, accountType, false,
@@ -1222,7 +1222,7 @@
private Integer getCredentialPermissionNotificationId(Account account, String authTokenType,
int uid) {
Integer id;
- UserAccounts accounts = getUserAccounts(UserId.getUserId(uid));
+ UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
synchronized (accounts.credentialsPermissionNotificationIds) {
final Pair<Pair<Account, String>, Integer> key =
new Pair<Pair<Account, String>, Integer>(
@@ -2269,7 +2269,7 @@
Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception());
return;
}
- UserAccounts accounts = getUserAccounts(UserId.getUserId(uid));
+ UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
synchronized (accounts.cacheLock) {
final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
db.beginTransaction();
@@ -2303,7 +2303,7 @@
Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception());
return;
}
- UserAccounts accounts = getUserAccounts(UserId.getUserId(uid));
+ UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
synchronized (accounts.cacheLock) {
final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
db.beginTransaction();
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 107e980..f874d56 100755
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -536,6 +536,9 @@
// The per-thread list of all active animations
private final ArrayList<ValueAnimator> mAnimations = new ArrayList<ValueAnimator>();
+ // Used in doAnimationFrame() to avoid concurrent modifications of mAnimations
+ private final ArrayList<ValueAnimator> mTmpAnimations = new ArrayList<ValueAnimator>();
+
// The per-thread set of animations to be started on the next animation frame
private final ArrayList<ValueAnimator> mPendingAnimations = new ArrayList<ValueAnimator>();
@@ -605,28 +608,18 @@
// Now process all active animations. The return value from animationFrame()
// tells the handler whether it should now be ended
int numAnims = mAnimations.size();
- int i = 0;
- while (i < numAnims) {
- ValueAnimator anim = mAnimations.get(i);
- if (anim.doAnimationFrame(frameTime)) {
+ for (int i = 0; i < numAnims; ++i) {
+ mTmpAnimations.add(mAnimations.get(i));
+ }
+ for (int i = 0; i < numAnims; ++i) {
+ ValueAnimator anim = mTmpAnimations.get(i);
+ if (mAnimations.contains(anim) && anim.doAnimationFrame(frameTime)) {
mEndingAnims.add(anim);
}
- if (mAnimations.size() == numAnims) {
- ++i;
- } else {
- // An animation might be canceled or ended by client code
- // during the animation frame. Check to see if this happened by
- // seeing whether the current index is the same as it was before
- // calling animationFrame(). Another approach would be to copy
- // animations to a temporary list and process that list instead,
- // but that entails garbage and processing overhead that would
- // be nice to avoid.
- --numAnims;
- mEndingAnims.remove(anim);
- }
}
+ mTmpAnimations.clear();
if (mEndingAnims.size() > 0) {
- for (i = 0; i < mEndingAnims.size(); ++i) {
+ for (int i = 0; i < mEndingAnims.size(); ++i) {
mEndingAnims.get(i).endAnimation(this);
}
mEndingAnims.clear();
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index c74f823..2c6d5d9 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -40,7 +40,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
-import android.os.UserId;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -529,7 +529,7 @@
throws SecurityException {
try {
return ActivityManagerNative.getDefault().getRecentTasks(maxNum,
- flags, UserId.myUserId());
+ flags, UserHandle.myUserId());
} catch (RemoteException e) {
// System dead, we will be dead too soon!
return null;
@@ -1843,12 +1843,12 @@
return PackageManager.PERMISSION_GRANTED;
}
// Isolated processes don't get any permissions.
- if (UserId.isIsolated(uid)) {
+ if (UserHandle.isIsolated(uid)) {
return PackageManager.PERMISSION_DENIED;
}
// 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 && UserId.isSameApp(uid, owningUid)) {
+ if (owningUid >= 0 && UserHandle.isSameApp(uid, owningUid)) {
return PackageManager.PERMISSION_GRANTED;
}
// If the target is not exported, then nobody else can get to it.
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 88e7344..3197a63 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -39,7 +39,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StrictMode;
-import android.os.UserId;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.util.Singleton;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 0789c60..7eb86f4 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -62,7 +62,7 @@
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.Trace;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.AndroidRuntimeException;
import android.util.DisplayMetrics;
import android.util.EventLog;
@@ -1696,7 +1696,7 @@
ApplicationInfo ai = null;
try {
ai = getPackageManager().getApplicationInfo(packageName,
- PackageManager.GET_SHARED_LIBRARY_FILES, UserId.myUserId());
+ PackageManager.GET_SHARED_LIBRARY_FILES, UserHandle.myUserId());
} catch (RemoteException e) {
// Ignore
}
@@ -1713,7 +1713,7 @@
boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0;
boolean securityViolation = includeCode && ai.uid != 0
&& ai.uid != Process.SYSTEM_UID && (mBoundApplication != null
- ? !UserId.isSameApp(ai.uid, mBoundApplication.appInfo.uid)
+ ? !UserHandle.isSameApp(ai.uid, mBoundApplication.appInfo.uid)
: true);
if ((flags&(Context.CONTEXT_INCLUDE_CODE
|Context.CONTEXT_IGNORE_SECURITY))
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 9b59e2c..115c867 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -50,7 +50,7 @@
import android.net.Uri;
import android.os.Process;
import android.os.RemoteException;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.Log;
import java.lang.ref.WeakReference;
@@ -69,7 +69,7 @@
public PackageInfo getPackageInfo(String packageName, int flags)
throws NameNotFoundException {
try {
- PackageInfo pi = mPM.getPackageInfo(packageName, flags, UserId.myUserId());
+ PackageInfo pi = mPM.getPackageInfo(packageName, flags, UserHandle.myUserId());
if (pi != null) {
return pi;
}
@@ -199,7 +199,7 @@
public ApplicationInfo getApplicationInfo(String packageName, int flags)
throws NameNotFoundException {
try {
- ApplicationInfo ai = mPM.getApplicationInfo(packageName, flags, UserId.myUserId());
+ ApplicationInfo ai = mPM.getApplicationInfo(packageName, flags, UserHandle.myUserId());
if (ai != null) {
return ai;
}
@@ -214,7 +214,7 @@
public ActivityInfo getActivityInfo(ComponentName className, int flags)
throws NameNotFoundException {
try {
- ActivityInfo ai = mPM.getActivityInfo(className, flags, UserId.myUserId());
+ ActivityInfo ai = mPM.getActivityInfo(className, flags, UserHandle.myUserId());
if (ai != null) {
return ai;
}
@@ -229,7 +229,7 @@
public ActivityInfo getReceiverInfo(ComponentName className, int flags)
throws NameNotFoundException {
try {
- ActivityInfo ai = mPM.getReceiverInfo(className, flags, UserId.myUserId());
+ ActivityInfo ai = mPM.getReceiverInfo(className, flags, UserHandle.myUserId());
if (ai != null) {
return ai;
}
@@ -244,7 +244,7 @@
public ServiceInfo getServiceInfo(ComponentName className, int flags)
throws NameNotFoundException {
try {
- ServiceInfo si = mPM.getServiceInfo(className, flags, UserId.myUserId());
+ ServiceInfo si = mPM.getServiceInfo(className, flags, UserHandle.myUserId());
if (si != null) {
return si;
}
@@ -259,7 +259,7 @@
public ProviderInfo getProviderInfo(ComponentName className, int flags)
throws NameNotFoundException {
try {
- ProviderInfo pi = mPM.getProviderInfo(className, flags, UserId.myUserId());
+ ProviderInfo pi = mPM.getProviderInfo(className, flags, UserHandle.myUserId());
if (pi != null) {
return pi;
}
@@ -424,7 +424,7 @@
@SuppressWarnings("unchecked")
@Override
public List<ApplicationInfo> getInstalledApplications(int flags) {
- int userId = UserId.getUserId(Process.myUid());
+ int userId = UserHandle.getUserId(Process.myUid());
try {
final List<ApplicationInfo> applicationInfos = new ArrayList<ApplicationInfo>();
ApplicationInfo lastItem = null;
@@ -448,7 +448,7 @@
return mPM.resolveIntent(
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- flags, UserId.myUserId());
+ flags, UserHandle.myUserId());
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
}
@@ -462,7 +462,7 @@
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
flags,
- UserId.myUserId());
+ UserHandle.myUserId());
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
}
@@ -494,7 +494,7 @@
try {
return mPM.queryIntentActivityOptions(caller, specifics,
specificTypes, intent, intent.resolveTypeIfNeeded(resolver),
- flags, UserId.myUserId());
+ flags, UserHandle.myUserId());
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
}
@@ -507,7 +507,7 @@
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
flags,
- UserId.myUserId());
+ UserHandle.myUserId());
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
}
@@ -520,7 +520,7 @@
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
flags,
- UserId.myUserId());
+ UserHandle.myUserId());
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
}
@@ -533,7 +533,7 @@
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
flags,
- UserId.myUserId());
+ UserHandle.myUserId());
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
}
@@ -543,7 +543,7 @@
public ProviderInfo resolveContentProvider(String name,
int flags) {
try {
- return mPM.resolveContentProvider(name, flags, UserId.myUserId());
+ return mPM.resolveContentProvider(name, flags, UserHandle.myUserId());
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
}
@@ -1033,7 +1033,7 @@
public void clearApplicationUserData(String packageName,
IPackageDataObserver observer) {
try {
- mPM.clearApplicationUserData(packageName, observer, UserId.myUserId());
+ mPM.clearApplicationUserData(packageName, observer, UserHandle.myUserId());
} catch (RemoteException e) {
// Should never happen!
}
@@ -1146,7 +1146,7 @@
public void setComponentEnabledSetting(ComponentName componentName,
int newState, int flags) {
try {
- mPM.setComponentEnabledSetting(componentName, newState, flags, UserId.myUserId());
+ mPM.setComponentEnabledSetting(componentName, newState, flags, UserHandle.myUserId());
} catch (RemoteException e) {
// Should never happen!
}
@@ -1155,7 +1155,7 @@
@Override
public int getComponentEnabledSetting(ComponentName componentName) {
try {
- return mPM.getComponentEnabledSetting(componentName, UserId.myUserId());
+ return mPM.getComponentEnabledSetting(componentName, UserHandle.myUserId());
} catch (RemoteException e) {
// Should never happen!
}
@@ -1166,7 +1166,7 @@
public void setApplicationEnabledSetting(String packageName,
int newState, int flags) {
try {
- mPM.setApplicationEnabledSetting(packageName, newState, flags, UserId.myUserId());
+ mPM.setApplicationEnabledSetting(packageName, newState, flags, UserHandle.myUserId());
} catch (RemoteException e) {
// Should never happen!
}
@@ -1175,7 +1175,7 @@
@Override
public int getApplicationEnabledSetting(String packageName) {
try {
- return mPM.getApplicationEnabledSetting(packageName, UserId.myUserId());
+ return mPM.getApplicationEnabledSetting(packageName, UserHandle.myUserId());
} catch (RemoteException e) {
// Should never happen!
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index fd4c304..08947a4 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -87,7 +87,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.UserId;
+import android.os.UserHandle;
import android.os.SystemVibrator;
import android.os.UserManager;
import android.os.storage.StorageManager;
@@ -906,12 +906,13 @@
/** @hide */
@Override
- public void startActivityAsUser(Intent intent, int userId) {
+ public void startActivityAsUser(Intent intent, UserHandle user) {
try {
ActivityManagerNative.getDefault().startActivityAsUser(
mMainThread.getApplicationThread(), intent,
intent.resolveTypeIfNeeded(getContentResolver()),
- null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, null, null, userId);
+ null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, null, null,
+ user.getIdentifier());
} catch (RemoteException re) {
}
}
@@ -931,12 +932,13 @@
/** @hide */
@Override
- public void startActivityAsUser(Intent intent, Bundle options, int userId) {
+ public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) {
try {
ActivityManagerNative.getDefault().startActivityAsUser(
mMainThread.getApplicationThread(), intent,
intent.resolveTypeIfNeeded(getContentResolver()),
- null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, null, options, userId);
+ null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, null, options,
+ user.getIdentifier());
} catch (RemoteException re) {
}
}
@@ -1062,19 +1064,19 @@
}
@Override
- public void sendBroadcastToUser(Intent intent, int userHandle) {
+ public void sendBroadcastAsUser(Intent intent, UserHandle user) {
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
intent.setAllowFds(false);
ActivityManagerNative.getDefault().broadcastIntent(mMainThread.getApplicationThread(),
intent, resolvedType, null, Activity.RESULT_OK, null, null, null, false, false,
- userHandle);
+ user.getIdentifier());
} catch (RemoteException e) {
}
}
@Override
- public void sendOrderedBroadcastToUser(Intent intent, int userHandle,
+ public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
BroadcastReceiver resultReceiver, Handler scheduler,
int initialCode, String initialData, Bundle initialExtras) {
IIntentReceiver rd = null;
@@ -1100,7 +1102,7 @@
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, rd,
initialCode, initialData, initialExtras, null,
- true, false, userHandle);
+ true, false, user.getIdentifier());
} catch (RemoteException e) {
}
}
@@ -1259,7 +1261,7 @@
@Override
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
- return bindService(service, conn, flags, UserId.getUserId(Process.myUid()));
+ return bindService(service, conn, flags, UserHandle.getUserId(Process.myUid()));
}
/** @hide */
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index f4195d6..1e89bb2 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -37,7 +37,7 @@
import android.os.RemoteException;
import android.os.StrictMode;
import android.os.Trace;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.AndroidRuntimeException;
import android.util.Slog;
import android.view.CompatibilityInfoHolder;
@@ -120,8 +120,8 @@
final int myUid = Process.myUid();
mResDir = aInfo.uid == myUid ? aInfo.sourceDir
: aInfo.publicSourceDir;
- if (!UserId.isSameUser(aInfo.uid, myUid) && !Process.isIsolated()) {
- aInfo.dataDir = PackageManager.getDataDirForUser(UserId.getUserId(myUid),
+ if (!UserHandle.isSameUser(aInfo.uid, myUid) && !Process.isIsolated()) {
+ aInfo.dataDir = PackageManager.getDataDirForUser(UserHandle.getUserId(myUid),
mPackageName);
}
mSharedLibraries = aInfo.sharedLibraryFiles;
@@ -195,7 +195,7 @@
ApplicationInfo ai = null;
try {
ai = ActivityThread.getPackageManager().getApplicationInfo(packageName,
- PackageManager.GET_SHARED_LIBRARY_FILES, UserId.myUserId());
+ PackageManager.GET_SHARED_LIBRARY_FILES, UserHandle.myUserId());
} catch (RemoteException e) {
throw new AssertionError(e);
}
@@ -358,7 +358,7 @@
IPackageManager pm = ActivityThread.getPackageManager();
android.content.pm.PackageInfo pi;
try {
- pi = pm.getPackageInfo(mPackageName, 0, UserId.myUserId());
+ pi = pm.getPackageInfo(mPackageName, 0, UserHandle.myUserId());
} catch (RemoteException e) {
throw new AssertionError(e);
}
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index c320ee3..a57c516 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -27,7 +27,7 @@
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.AndroidException;
/**
@@ -639,22 +639,22 @@
/**
* Return the user handle of the application that created this
* PendingIntent, that is the user under which you will actually be
- * sending the Intent. The returned integer is supplied by the system, so
+ * sending the Intent. The returned UserHandle is supplied by the system, so
* that an application can not spoof its user. See
* {@link android.os.Process#myUserHandle() Process.myUserHandle()} for
* more explanation of user handles.
*
- * @return The user handle of the PendingIntent, or -1 if there is
+ * @return The user handle of the PendingIntent, or null if there is
* none associated with it.
*/
- public int getTargetUserHandle() {
+ public UserHandle getTargetUserHandle() {
try {
int uid = ActivityManagerNative.getDefault()
.getUidForIntentSender(mTarget);
- return uid > 0 ? UserId.getUserId(uid) : -1;
+ return uid > 0 ? new UserHandle(UserHandle.getUserId(uid)) : null;
} catch (RemoteException e) {
// Should never happen.
- return -1;
+ return null;
}
}
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index b8c9937..43a163d 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -31,7 +31,7 @@
import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.UserId;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
@@ -847,7 +847,7 @@
* @hide
*/
public Intent getAssistIntent(Context context) {
- return getAssistIntent(context, UserId.myUserId());
+ return getAssistIntent(context, UserHandle.myUserId());
}
/**
diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java
index 615e8ce..201d7b2 100644
--- a/core/java/android/app/SharedPreferencesImpl.java
+++ b/core/java/android/app/SharedPreferencesImpl.java
@@ -17,7 +17,6 @@
package android.app;
import android.content.SharedPreferences;
-import android.os.FileUtils.FileStatus;
import android.os.FileUtils;
import android.os.Looper;
import android.util.Log;
@@ -45,6 +44,11 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
+import libcore.io.ErrnoException;
+import libcore.io.IoUtils;
+import libcore.io.Libcore;
+import libcore.io.StructStat;
+
final class SharedPreferencesImpl implements SharedPreferences {
private static final String TAG = "SharedPreferencesImpl";
private static final boolean DEBUG = false;
@@ -105,26 +109,32 @@
}
Map map = null;
- FileStatus stat = new FileStatus();
- if (FileUtils.getFileStatus(mFile.getPath(), stat) && mFile.canRead()) {
- try {
- BufferedInputStream str = new BufferedInputStream(
- new FileInputStream(mFile), 16*1024);
- map = XmlUtils.readMapXml(str);
- str.close();
- } catch (XmlPullParserException e) {
- Log.w(TAG, "getSharedPreferences", e);
- } catch (FileNotFoundException e) {
- Log.w(TAG, "getSharedPreferences", e);
- } catch (IOException e) {
- Log.w(TAG, "getSharedPreferences", e);
+ StructStat stat = null;
+ try {
+ stat = Libcore.os.stat(mFile.getPath());
+ if (mFile.canRead()) {
+ BufferedInputStream str = null;
+ try {
+ str = new BufferedInputStream(
+ new FileInputStream(mFile), 16*1024);
+ map = XmlUtils.readMapXml(str);
+ } catch (XmlPullParserException e) {
+ Log.w(TAG, "getSharedPreferences", e);
+ } catch (FileNotFoundException e) {
+ Log.w(TAG, "getSharedPreferences", e);
+ } catch (IOException e) {
+ Log.w(TAG, "getSharedPreferences", e);
+ } finally {
+ IoUtils.closeQuietly(str);
+ }
}
+ } catch (ErrnoException e) {
}
mLoaded = true;
if (map != null) {
mMap = map;
- mStatTimestamp = stat.mtime;
- mStatSize = stat.size;
+ mStatTimestamp = stat.st_mtime;
+ mStatSize = stat.st_size;
} else {
mMap = new HashMap<String, Object>();
}
@@ -155,12 +165,21 @@
return false;
}
}
- FileStatus stat = new FileStatus();
- if (!FileUtils.getFileStatus(mFile.getPath(), stat)) {
+
+ final StructStat stat;
+ try {
+ /*
+ * Metadata operations don't usually count as a block guard
+ * violation, but we explicitly want this one.
+ */
+ BlockGuard.getThreadPolicy().onReadFromDisk();
+ stat = Libcore.os.stat(mFile.getPath());
+ } catch (ErrnoException e) {
return true;
}
+
synchronized (this) {
- return mStatTimestamp != stat.mtime || mStatSize != stat.size;
+ return mStatTimestamp != stat.st_mtime || mStatSize != stat.st_size;
}
}
@@ -577,12 +596,14 @@
FileUtils.sync(str);
str.close();
ContextImpl.setFilePermissionsFromMode(mFile.getPath(), mMode, 0);
- FileStatus stat = new FileStatus();
- if (FileUtils.getFileStatus(mFile.getPath(), stat)) {
+ try {
+ final StructStat stat = Libcore.os.stat(mFile.getPath());
synchronized (this) {
- mStatTimestamp = stat.mtime;
- mStatSize = stat.size;
+ mStatTimestamp = stat.st_mtime;
+ mStatSize = stat.st_size;
}
+ } catch (ErrnoException e) {
+ // Do nothing
}
// Writing was successful, delete the backup file if there is one.
mBackupFile.delete();
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index b22179e..8a69c3a 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -35,7 +35,7 @@
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.Log;
import java.io.File;
diff --git a/core/java/android/content/ContentService.java b/core/java/android/content/ContentService.java
index 1a07504..472fe94 100644
--- a/core/java/android/content/ContentService.java
+++ b/core/java/android/content/ContentService.java
@@ -26,7 +26,7 @@
import android.os.Parcel;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.Log;
import android.util.SparseIntArray;
import android.Manifest;
@@ -168,7 +168,7 @@
+ ", syncToNetwork " + syncToNetwork);
}
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
// This makes it so that future permission checks will be in the context of this
// process rather than the caller's process. We will restore this before returning.
long identityToken = clearCallingIdentity();
@@ -236,7 +236,7 @@
public void requestSync(Account account, String authority, Bundle extras) {
ContentResolver.validateSyncExtrasBundle(extras);
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
// This makes it so that future permission checks will be in the context of this
// process rather than the caller's process. We will restore this before returning.
@@ -259,7 +259,7 @@
* @param authority filter the pending and active syncs to cancel using this authority
*/
public void cancelSync(Account account, String authority) {
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
// This makes it so that future permission checks will be in the context of this
// process rather than the caller's process. We will restore this before returning.
@@ -294,7 +294,7 @@
public boolean getSyncAutomatically(Account account, String providerName) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
"no permission to read the sync settings");
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
long identityToken = clearCallingIdentity();
try {
@@ -312,7 +312,7 @@
public void setSyncAutomatically(Account account, String providerName, boolean sync) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
"no permission to write the sync settings");
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
long identityToken = clearCallingIdentity();
try {
@@ -330,7 +330,7 @@
long pollFrequency) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
"no permission to write the sync settings");
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
long identityToken = clearCallingIdentity();
try {
@@ -344,7 +344,7 @@
public void removePeriodicSync(Account account, String authority, Bundle extras) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
"no permission to write the sync settings");
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
long identityToken = clearCallingIdentity();
try {
@@ -358,7 +358,7 @@
public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
"no permission to read the sync settings");
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
long identityToken = clearCallingIdentity();
try {
@@ -372,7 +372,7 @@
public int getIsSyncable(Account account, String providerName) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
"no permission to read the sync settings");
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
long identityToken = clearCallingIdentity();
try {
@@ -390,7 +390,7 @@
public void setIsSyncable(Account account, String providerName, int syncable) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
"no permission to write the sync settings");
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
long identityToken = clearCallingIdentity();
try {
@@ -407,7 +407,7 @@
public boolean getMasterSyncAutomatically() {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
"no permission to read the sync settings");
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
long identityToken = clearCallingIdentity();
try {
@@ -424,7 +424,7 @@
public void setMasterSyncAutomatically(boolean flag) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
"no permission to write the sync settings");
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
long identityToken = clearCallingIdentity();
try {
@@ -440,7 +440,7 @@
public boolean isSyncActive(Account account, String authority) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
"no permission to read the sync stats");
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
long identityToken = clearCallingIdentity();
try {
@@ -458,7 +458,7 @@
public List<SyncInfo> getCurrentSyncs() {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
"no permission to read the sync stats");
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
long identityToken = clearCallingIdentity();
try {
@@ -471,7 +471,7 @@
public SyncStatusInfo getSyncStatus(Account account, String authority) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
"no permission to read the sync stats");
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
long identityToken = clearCallingIdentity();
try {
@@ -489,7 +489,7 @@
public boolean isSyncPending(Account account, String authority) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
"no permission to read the sync stats");
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
long identityToken = clearCallingIdentity();
try {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index a90142a..a70bf6c 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -32,6 +32,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
+import android.os.UserHandle;
import android.util.AttributeSet;
import java.io.File;
@@ -855,11 +856,11 @@
* Same as {@link #startActivity(Intent)}, but for a specific user. It requires holding
* the {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission.
* @param intent The description of the activity to start.
- * @param userId The user id of the user to start this activity for.
+ * @param user The UserHandle of the user to start this activity for.
* @throws ActivityNotFoundException
* @hide
*/
- public void startActivityAsUser(Intent intent, int userId) {
+ public void startActivityAsUser(Intent intent, UserHandle user) {
throw new RuntimeException("Not implemented. Must override in a subclass.");
}
@@ -898,11 +899,11 @@
* May be null if there are no options. See {@link android.app.ActivityOptions}
* for how to build the Bundle supplied here; there are no supported definitions
* for building it manually.
- * @param userId The user id of the user to start this activity for.
+ * @param user The UserHandle of the user to start this activity for.
* @throws ActivityNotFoundException
* @hide
*/
- public void startActivityAsUser(Intent intent, Bundle options, int userId) {
+ public void startActivityAsUser(Intent intent, Bundle options, UserHandle userId) {
throw new RuntimeException("Not implemented. Must override in a subclass.");
}
@@ -1119,10 +1120,10 @@
* requires holding the {@link android.Manifest.permission#INTERACT_ACROSS_USERS}
* permission.
* @param intent The intent to broadcast
- * @param userHandle User to send the intent to.
+ * @param user UserHandle to send the intent to.
* @see #sendBroadcast(Intent)
*/
- public abstract void sendBroadcastToUser(Intent intent, int userHandle);
+ public abstract void sendBroadcastAsUser(Intent intent, UserHandle user);
/**
* Same as
@@ -1136,7 +1137,7 @@
*
* @param intent The Intent to broadcast; all receivers matching this
* Intent will receive the broadcast.
- * @param userHandle User to send the intent to.
+ * @param user UserHandle to send the intent to.
* @param resultReceiver Your own BroadcastReceiver to treat as the final
* receiver of the broadcast.
* @param scheduler A custom Handler with which to schedule the
@@ -1151,7 +1152,7 @@
*
* @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
*/
- public abstract void sendOrderedBroadcastToUser(Intent intent, int userHandle,
+ public abstract void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
BroadcastReceiver resultReceiver, Handler scheduler,
int initialCode, String initialData, Bundle initialExtras);
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index fdf60ab..75842be 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -30,6 +30,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
+import android.os.UserHandle;
import java.io.File;
import java.io.FileInputStream;
@@ -279,8 +280,8 @@
/** @hide */
@Override
- public void startActivityAsUser(Intent intent, int userId) {
- mBase.startActivityAsUser(intent, userId);
+ public void startActivityAsUser(Intent intent, UserHandle user) {
+ mBase.startActivityAsUser(intent, user);
}
@Override
@@ -290,8 +291,8 @@
/** @hide */
@Override
- public void startActivityAsUser(Intent intent, Bundle options, int userId) {
- mBase.startActivityAsUser(intent, options, userId);
+ public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) {
+ mBase.startActivityAsUser(intent, options, user);
}
@Override
@@ -347,15 +348,15 @@
}
@Override
- public void sendBroadcastToUser(Intent intent, int userHandle) {
- mBase.sendBroadcastToUser(intent, userHandle);
+ public void sendBroadcastAsUser(Intent intent, UserHandle user) {
+ mBase.sendBroadcastAsUser(intent, user);
}
@Override
- public void sendOrderedBroadcastToUser(Intent intent, int userHandle,
+ public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
BroadcastReceiver resultReceiver, Handler scheduler,
int initialCode, String initialData, Bundle initialExtras) {
- mBase.sendOrderedBroadcastToUser(intent, userHandle, resultReceiver,
+ mBase.sendOrderedBroadcastAsUser(intent, user, resultReceiver,
scheduler, initialCode, initialData, initialExtras);
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index d325186..0190555 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2279,24 +2279,24 @@
"android.intent.action.PRE_BOOT_COMPLETED";
/**
- * Broadcast sent to the system when a user is added. Carries an extra EXTRA_USERID that has the
- * userid of the new user.
+ * Broadcast sent to the system when a user is added. Carries an extra EXTRA_USER_HANDLE that has the
+ * userHandle of the new user.
* @hide
*/
public static final String ACTION_USER_ADDED =
"android.intent.action.USER_ADDED";
/**
- * Broadcast sent to the system when a user is removed. Carries an extra EXTRA_USERID that has
- * the userid of the user.
+ * Broadcast sent to the system when a user is removed. Carries an extra EXTRA_USER_HANDLE that has
+ * the userHandle of the user.
* @hide
*/
public static final String ACTION_USER_REMOVED =
"android.intent.action.USER_REMOVED";
/**
- * Broadcast sent to the system when the user switches. Carries an extra EXTRA_USERID that has
- * the userid of the user to become the current one.
+ * Broadcast sent to the system when the user switches. Carries an extra EXTRA_USER_HANDLE that has
+ * the userHandle of the user to become the current one.
* @hide
*/
public static final String ACTION_USER_SWITCHED =
@@ -2852,12 +2852,12 @@
"android.intent.extra.LOCAL_ONLY";
/**
- * The userid carried with broadcast intents related to addition, removal and switching of users
+ * The userHandle carried with broadcast intents related to addition, removal and switching of users
* - {@link #ACTION_USER_ADDED}, {@link #ACTION_USER_REMOVED} and {@link #ACTION_USER_SWITCHED}.
* @hide
*/
- public static final String EXTRA_USERID =
- "android.intent.extra.user_id";
+ public static final String EXTRA_USER_HANDLE =
+ "android.intent.extra.user_handle";
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java
index 9618645..079241a 100644
--- a/core/java/android/content/IntentSender.java
+++ b/core/java/android/content/IntentSender.java
@@ -27,7 +27,7 @@
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.AndroidException;
@@ -245,22 +245,22 @@
/**
* Return the user handle of the application that created this
* PendingIntent, that is the user under which you will actually be
- * sending the Intent. The returned integer is supplied by the system, so
+ * sending the Intent. The returned UserHandle is supplied by the system, so
* that an application can not spoof its user. See
* {@link android.os.Process#myUserHandle() Process.myUserHandle()} for
* more explanation of user handles.
*
- * @return The user handle of the PendingIntent, or -1 if there is
+ * @return The user handle of the PendingIntent, or null if there is
* none associated with it.
*/
- public int getTargetUserHandle() {
+ public UserHandle getTargetUserHandle() {
try {
int uid = ActivityManagerNative.getDefault()
.getUidForIntentSender(mTarget);
- return uid > 0 ? UserId.getUserId(uid) : -1;
+ return uid > 0 ? new UserHandle(UserHandle.getUserId(uid)) : null;
} catch (RemoteException e) {
// Should never happen.
- return -1;
+ return null;
}
}
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index e6303b9..ee075b4 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -52,7 +52,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.os.UserId;
+import android.os.UserHandle;
import android.os.UserManager;
import android.os.WorkSource;
import android.provider.Settings;
@@ -174,7 +174,7 @@
Log.v(TAG, "Internal storage is low.");
}
mStorageIsLow = true;
- cancelActiveSync(null /* any account */, UserId.USER_ALL,
+ cancelActiveSync(null /* any account */, UserHandle.USER_ALL,
null /* any authority */);
} else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
@@ -195,7 +195,7 @@
private BroadcastReceiver mBackgroundDataSettingChanged = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
if (getConnectivityManager().getBackgroundDataSetting()) {
- scheduleSync(null /* account */, UserId.USER_ALL, null /* authority */,
+ scheduleSync(null /* account */, UserHandle.USER_ALL, null /* authority */,
new Bundle(), 0 /* delay */,
false /* onlyThoseWithUnknownSyncableState */);
}
@@ -287,7 +287,7 @@
// a chance to set their syncable state.
boolean onlyThoseWithUnkownSyncableState = justBootedUp;
- scheduleSync(null, UserId.USER_ALL, null, null, 0 /* no delay */,
+ scheduleSync(null, UserHandle.USER_ALL, null, null, 0 /* no delay */,
onlyThoseWithUnkownSyncableState);
}
}
@@ -371,7 +371,7 @@
mSyncAdapters.setListener(new RegisteredServicesCacheListener<SyncAdapterType>() {
public void onServiceChanged(SyncAdapterType type, boolean removed) {
if (!removed) {
- scheduleSync(null, UserId.USER_ALL, type.authority, null, 0 /* no delay */,
+ scheduleSync(null, UserHandle.USER_ALL, type.authority, null, 0 /* no delay */,
false /* onlyThoseWithUnkownSyncableState */);
}
}
@@ -517,7 +517,7 @@
}
AccountAndUser[] accounts;
- if (requestedAccount != null && userId != UserId.USER_ALL) {
+ if (requestedAccount != null && userId != UserHandle.USER_ALL) {
accounts = new AccountAndUser[] { new AccountAndUser(requestedAccount, userId) };
} else {
// if the accounts aren't configured yet then we can't support an account-less
@@ -895,7 +895,7 @@
}
private void onUserRemoved(Intent intent) {
- int userId = intent.getIntExtra(Intent.EXTRA_USERID, -1);
+ int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
if (userId == -1) return;
// Clean up the storage engine database
@@ -2180,7 +2180,7 @@
}
}
// check if the userid matches
- if (userId != UserId.USER_ALL
+ if (userId != UserHandle.USER_ALL
&& userId != activeSyncContext.mSyncOperation.userId) {
continue;
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index d906401..ac75040 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -28,7 +28,7 @@
import android.os.Build;
import android.os.Bundle;
import android.os.PatternMatcher;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.AttributeSet;
import android.util.Base64;
import android.util.DisplayMetrics;
@@ -249,7 +249,7 @@
return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime,
grantedPermissions, false, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
- UserId.getCallingUserId());
+ UserHandle.getCallingUserId());
}
/**
@@ -263,7 +263,7 @@
HashSet<String> grantedPermissions, boolean stopped, int enabledState) {
return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime,
- grantedPermissions, stopped, enabledState, UserId.getCallingUserId());
+ grantedPermissions, stopped, enabledState, UserHandle.getCallingUserId());
}
public static PackageInfo generatePackageInfo(PackageParser.Package p,
@@ -3478,7 +3478,7 @@
public static ApplicationInfo generateApplicationInfo(Package p, int flags, boolean stopped,
int enabledState) {
- return generateApplicationInfo(p, flags, stopped, enabledState, UserId.getCallingUserId());
+ return generateApplicationInfo(p, flags, stopped, enabledState, UserHandle.getCallingUserId());
}
public static ApplicationInfo generateApplicationInfo(Package p, int flags,
@@ -3508,7 +3508,7 @@
// 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.uid = UserHandle.getUid(userId, ai.uid);
ai.dataDir = PackageManager.getDataDirForUser(userId, ai.packageName);
}
if ((flags & PackageManager.GET_META_DATA) != 0) {
@@ -3616,7 +3616,7 @@
int enabledState, int userId) {
if (s == null) return null;
if (!copyNeeded(flags, s.owner, enabledState, s.metaData)
- && userId == UserId.getUserId(s.info.applicationInfo.uid)) {
+ && userId == UserHandle.getUserId(s.info.applicationInfo.uid)) {
return s.info;
}
// Make shallow copies so we can store the metadata safely
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 638e273..060a235 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -53,6 +53,7 @@
public static final int FLAG_RESTRICTED = 0x00000008;
public int id;
+ public int serialNumber;
public String name;
public String iconPath;
public int flags;
@@ -88,6 +89,7 @@
iconPath = orig.iconPath;
id = orig.id;
flags = orig.flags;
+ serialNumber = orig.serialNumber;
}
@Override
@@ -104,6 +106,7 @@
dest.writeString(name);
dest.writeString(iconPath);
dest.writeInt(flags);
+ dest.writeInt(serialNumber);
}
public static final Parcelable.Creator<UserInfo> CREATOR
@@ -121,5 +124,6 @@
name = source.readString();
iconPath = source.readString();
flags = source.readInt();
+ serialNumber = source.readInt();
}
}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 7b51119..5d40456 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -82,7 +82,7 @@
* @hide
*/
public static final int getOrigCallingUid() {
- if (UserId.MU_ENABLED) {
+ if (UserHandle.MU_ENABLED) {
return getOrigCallingUidNative();
} else {
return getCallingUid();
@@ -97,7 +97,7 @@
* @hide
*/
public static final int getOrigCallingUser() {
- return UserId.getUserId(getOrigCallingUid());
+ return UserHandle.getUserId(getOrigCallingUid());
}
/**
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 6c1445d..7c103aa 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -28,9 +28,6 @@
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;
-import libcore.io.Os;
-import libcore.io.StructStat;
-
/**
* Tools for managing files. Not for public consumption.
* @hide
@@ -50,58 +47,12 @@
public static final int S_IROTH = 00004;
public static final int S_IWOTH = 00002;
public static final int S_IXOTH = 00001;
-
-
- /**
- * File status information. This class maps directly to the POSIX stat structure.
- * @deprecated use {@link StructStat} instead.
- * @hide
- */
- @Deprecated
- public static final class FileStatus {
- public int dev;
- public int ino;
- public int mode;
- public int nlink;
- public int uid;
- public int gid;
- public int rdev;
- public long size;
- public int blksize;
- public long blocks;
- public long atime;
- public long mtime;
- public long ctime;
- }
-
- /**
- * Get the status for the given path. This is equivalent to the POSIX stat(2) system call.
- * @param path The path of the file to be stat'd.
- * @param status Optional argument to fill in. It will only fill in the status if the file
- * exists.
- * @return true if the file exists and false if it does not exist. If you do not have
- * permission to stat the file, then this method will return false.
- * @deprecated use {@link Os#stat(String)} instead.
- */
- @Deprecated
- public static boolean getFileStatus(String path, FileStatus status) {
- StrictMode.noteDiskRead();
- return getFileStatusNative(path, status);
- }
-
- private static native boolean getFileStatusNative(String path, FileStatus status);
/** Regular expression for safe filenames: no spaces or metacharacters */
private static final Pattern SAFE_FILENAME_PATTERN = Pattern.compile("[\\w%+,./=_-]+");
public static native int setPermissions(String file, int mode, int uid, int gid);
- /**
- * @deprecated use {@link Os#stat(String)} instead.
- */
- @Deprecated
- public static native int getPermissions(String file, int[] outPermissions);
-
public static native int setUMask(int mask);
/** returns the FAT file system volume ID for the volume mounted
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index cb1b962..c7a8493 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -33,4 +33,6 @@
void setGuestEnabled(boolean enable);
boolean isGuestEnabled();
void wipeUser(int userHandle);
+ int getUserSerialNumber(int userHandle);
+ int getUserHandle(int userSerialNumber);
}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 93860aa..3513bdc 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -376,12 +376,13 @@
public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
- int debugFlags, int targetSdkVersion,
+ int debugFlags, int mountExternal,
+ int targetSdkVersion,
String seInfo,
String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
- debugFlags, targetSdkVersion, seInfo, zygoteArgs);
+ debugFlags, mountExternal, targetSdkVersion, seInfo, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
@@ -553,7 +554,8 @@
final String niceName,
final int uid, final int gid,
final int[] gids,
- int debugFlags, int targetSdkVersion,
+ int debugFlags, int mountExternal,
+ int targetSdkVersion,
String seInfo,
String[] extraArgs)
throws ZygoteStartFailedEx {
@@ -580,6 +582,11 @@
if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
argsForZygote.add("--enable-assert");
}
+ if (mountExternal == Zygote.MOUNT_EXTERNAL_SINGLEUSER) {
+ argsForZygote.add("--mount-external-singleuser");
+ } else if (mountExternal == Zygote.MOUNT_EXTERNAL_MULTIUSER) {
+ argsForZygote.add("--mount-external-multiuser");
+ }
argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
//TODO optionally enable debuger
@@ -648,13 +655,13 @@
public static final native int myUid();
/**
- * Returns the identifier of this process's user handle. This is the
+ * Returns this process's user handle. This is the
* user the process is running under. It is distinct from
* {@link #myUid()} in that a particular user will have multiple
* distinct apps running under it each with their own uid.
*/
- public static final int myUserHandle() {
- return UserId.getUserId(myUid());
+ public static final UserHandle myUserHandle() {
+ return new UserHandle(UserHandle.getUserId(myUid()));
}
/**
@@ -662,7 +669,7 @@
* @hide
*/
public static final boolean isIsolated() {
- int uid = UserId.getAppId(myUid());
+ int uid = UserHandle.getAppId(myUid());
return uid >= FIRST_ISOLATED_UID && uid <= LAST_ISOLATED_UID;
}
diff --git a/core/java/android/os/SELinux.java b/core/java/android/os/SELinux.java
index 90cfa37..c05a974 100644
--- a/core/java/android/os/SELinux.java
+++ b/core/java/android/os/SELinux.java
@@ -1,5 +1,25 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package android.os;
+import android.util.Slog;
+
+import java.io.IOException;
+import java.io.File;
import java.io.FileDescriptor;
/**
@@ -9,6 +29,8 @@
*/
public class SELinux {
+ private static final String TAG = "SELinux";
+
/**
* Determine whether SELinux is disabled or enabled.
* @return a boolean indicating whether SELinux is enabled.
@@ -102,4 +124,53 @@
* @return a boolean indicating whether permission was granted.
*/
public static final native boolean checkSELinuxAccess(String scon, String tcon, String tclass, String perm);
+
+ /**
+ * Restores a file to its default SELinux security context.
+ * If the system is not compiled with SELinux, then {@code true}
+ * is automatically returned.
+ * If SELinux is compiled in, but disabled, then {@code true} is
+ * returned.
+ *
+ * @param pathname The pathname of the file to be relabeled.
+ * @return a boolean indicating whether the relabeling succeeded.
+ * @exception NullPointerException if the pathname is a null object.
+ */
+ public static boolean restorecon(String pathname) throws NullPointerException {
+ if (pathname == null) { throw new NullPointerException(); }
+ return native_restorecon(pathname);
+ }
+
+ /**
+ * Restores a file to its default SELinux security context.
+ * If the system is not compiled with SELinux, then {@code true}
+ * is automatically returned.
+ * If SELinux is compiled in, but disabled, then {@code true} is
+ * returned.
+ *
+ * @param pathname The pathname of the file to be relabeled.
+ * @return a boolean indicating whether the relabeling succeeded.
+ */
+ private static native boolean native_restorecon(String pathname);
+
+ /**
+ * Restores a file to its default SELinux security context.
+ * If the system is not compiled with SELinux, then {@code true}
+ * is automatically returned.
+ * If SELinux is compiled in, but disabled, then {@code true} is
+ * returned.
+ *
+ * @param file The File object representing the path to be relabeled.
+ * @return a boolean indicating whether the relabeling succeeded.
+ * @exception NullPointerException if the file is a null object.
+ */
+ public static boolean restorecon(File file) throws NullPointerException {
+ try {
+ return native_restorecon(file.getCanonicalPath());
+ } catch (IOException e) {
+ Slog.e(TAG, "Error getting canonical path. Restorecon failed for " +
+ file.getPath(), e);
+ return false;
+ }
+ }
}
diff --git a/core/java/android/os/StatFs.java b/core/java/android/os/StatFs.java
index 912bfdf..ca7fdba 100644
--- a/core/java/android/os/StatFs.java
+++ b/core/java/android/os/StatFs.java
@@ -16,59 +16,77 @@
package android.os;
+import libcore.io.ErrnoException;
+import libcore.io.Libcore;
+import libcore.io.StructStatFs;
+
/**
- * Retrieve overall information about the space on a filesystem. This is a
- * Wrapper for Unix statfs().
+ * Retrieve overall information about the space on a filesystem. This is a
+ * wrapper for Unix statfs().
*/
public class StatFs {
- /**
- * Construct a new StatFs for looking at the stats of the
- * filesystem at <var>path</var>. Upon construction, the stat of
- * the file system will be performed, and the values retrieved available
- * from the methods on this class.
- *
- * @param path A path in the desired file system to state.
- */
- public StatFs(String path) { native_setup(path); }
-
- /**
- * Perform a restat of the file system referenced by this object. This
- * is the same as re-constructing the object with the same file system
- * path, and the new stat values are available upon return.
- */
- public void restat(String path) { native_restat(path); }
-
- @Override
- protected void finalize() { native_finalize(); }
+ private StructStatFs mStat;
/**
- * The size, in bytes, of a block on the file system. This corresponds
- * to the Unix statfs.f_bsize field.
+ * Construct a new StatFs for looking at the stats of the filesystem at
+ * {@code path}. Upon construction, the stat of the file system will be
+ * performed, and the values retrieved available from the methods on this
+ * class.
+ *
+ * @param path path in the desired file system to stat.
*/
- public native int getBlockSize();
+ public StatFs(String path) {
+ mStat = doStat(path);
+ }
+
+ private static StructStatFs doStat(String path) {
+ try {
+ return Libcore.os.statfs(path);
+ } catch (ErrnoException e) {
+ throw new IllegalArgumentException("Invalid path: " + path, e);
+ }
+ }
/**
- * The total number of blocks on the file system. This corresponds
- * to the Unix statfs.f_blocks field.
+ * Perform a restat of the file system referenced by this object. This is
+ * the same as re-constructing the object with the same file system path,
+ * and the new stat values are available upon return.
*/
- public native int getBlockCount();
+ public void restat(String path) {
+ mStat = doStat(path);
+ }
+
+ /**
+ * The size, in bytes, of a block on the file system. This corresponds to
+ * the Unix {@code statfs.f_bsize} field.
+ */
+ public int getBlockSize() {
+ return (int) mStat.f_bsize;
+ }
+
+ /**
+ * The total number of blocks on the file system. This corresponds to the
+ * Unix {@code statfs.f_blocks} field.
+ */
+ public int getBlockCount() {
+ return (int) mStat.f_blocks;
+ }
/**
* The total number of blocks that are free on the file system, including
- * reserved blocks (that are not available to normal applications). This
- * corresponds to the Unix statfs.f_bfree field. Most applications will
- * want to use {@link #getAvailableBlocks()} instead.
+ * reserved blocks (that are not available to normal applications). This
+ * corresponds to the Unix {@code statfs.f_bfree} field. Most applications
+ * will want to use {@link #getAvailableBlocks()} instead.
*/
- public native int getFreeBlocks();
+ public int getFreeBlocks() {
+ return (int) mStat.f_bfree;
+ }
/**
* The number of blocks that are free on the file system and available to
- * applications. This corresponds to the Unix statfs.f_bavail field.
+ * applications. This corresponds to the Unix {@code statfs.f_bavail} field.
*/
- public native int getAvailableBlocks();
-
- private int mNativeContext;
- private native void native_restat(String path);
- private native void native_setup(String path);
- private native void native_finalize();
+ public int getAvailableBlocks() {
+ return (int) mStat.f_bavail;
+ }
}
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
new file mode 100644
index 0000000..0843d85
--- /dev/null
+++ b/core/java/android/os/UserHandle.java
@@ -0,0 +1,228 @@
+/*
+ * 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;
+
+/**
+ * Representation of a user on the device.
+ */
+public final class UserHandle implements Parcelable {
+ /**
+ * @hide Range of uids allocated for a user.
+ */
+ public static final int PER_USER_RANGE = 100000;
+
+ /** @hide A user id to indicate all users on the device */
+ public static final int USER_ALL = -1;
+
+ /** @hide A user id to indicate the currently active user */
+ public static final int USER_CURRENT = -2;
+
+ /** @hide An undefined user id */
+ public static final int USER_NULL = -10000;
+
+ /** @hide A user id constant to indicate the "owner" user of the device */
+ public static final int USER_OWNER = 0;
+
+ /**
+ * @hide Enable multi-user related side effects. Set this to false if
+ * there are problems with single user use-cases.
+ */
+ public static final boolean MU_ENABLED = true;
+
+ final int mHandle;
+
+ /**
+ * 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);
+ }
+
+ /** @hide */
+ public static final boolean isIsolated(int uid) {
+ uid = getAppId(uid);
+ return uid >= Process.FIRST_ISOLATED_UID && uid <= Process.LAST_ISOLATED_UID;
+ }
+
+ /** @hide */
+ public static boolean isApp(int uid) {
+ if (uid > 0) {
+ uid = UserHandle.getAppId(uid);
+ return uid >= Process.FIRST_APPLICATION_UID && uid <= Process.LAST_APPLICATION_UID;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * 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;
+ }
+ }
+
+ /** @hide */
+ public static final int getCallingUserId() {
+ return getUserId(Binder.getCallingUid());
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Returns the user id of the current process
+ * @return user id of the current process
+ * @hide
+ */
+ public static final int myUserId() {
+ return getUserId(Process.myUid());
+ }
+
+ /** @hide */
+ public UserHandle(int h) {
+ mHandle = h;
+ }
+
+ /** @hide */
+ public int getIdentifier() {
+ return mHandle;
+ }
+
+ @Override
+ public String toString() {
+ return "UserHandle{" + mHandle + "}";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ try {
+ if (obj != null) {
+ UserHandle other = (UserHandle)obj;
+ return mHandle == other.mHandle;
+ }
+ } catch (ClassCastException e) {
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return mHandle;
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mHandle);
+ }
+
+ /**
+ * Write a UserHandle to a Parcel, handling null pointers. Must be
+ * read with {@link #readFromParcel(Parcel)}.
+ *
+ * @param h The UserHandle to be written.
+ * @param out The Parcel in which the UserHandle will be placed.
+ *
+ * @see #readFromParcel(Parcel)
+ */
+ public static void writeToParcel(UserHandle h, Parcel out) {
+ if (h != null) {
+ h.writeToParcel(out, 0);
+ } else {
+ out.writeInt(USER_NULL);
+ }
+ }
+
+ /**
+ * Read a UserHandle from a Parcel that was previously written
+ * with {@link #writeToParcel(UserHandle, Parcel)}, returning either
+ * a null or new object as appropriate.
+ *
+ * @param in The Parcel from which to read the UserHandle
+ * @return Returns a new UserHandle matching the previously written
+ * object, or null if a null had been written.
+ *
+ * @see #writeToParcel(UserHandle, Parcel)
+ */
+ public static UserHandle readFromParcel(Parcel in) {
+ int h = in.readInt();
+ return h != USER_NULL ? new UserHandle(h) : null;
+ }
+
+ public static final Parcelable.Creator<UserHandle> CREATOR
+ = new Parcelable.Creator<UserHandle>() {
+ public UserHandle createFromParcel(Parcel in) {
+ return new UserHandle(in);
+ }
+
+ public UserHandle[] newArray(int size) {
+ return new UserHandle[size];
+ }
+ };
+
+ /**
+ * Instantiate a new UserHandle from the data in a Parcel that was
+ * previously written with {@link #writeToParcel(Parcel, int)}. Note that you
+ * must not use this with data written by
+ * {@link #writeToParcel(UserHandle, Parcel)} since it is not possible
+ * to handle a null UserHandle here.
+ *
+ * @param in The Parcel containing the previously written UserHandle,
+ * positioned at the location in the buffer where it was written.
+ */
+ public UserHandle(Parcel in) {
+ mHandle = in.readInt();
+ }
+}
diff --git a/core/java/android/os/UserId.java b/core/java/android/os/UserId.java
deleted file mode 100644
index 18a3062..0000000
--- a/core/java/android/os/UserId.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * 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;
-
- /** A user id to indicate all users on the device */
- public static final int USER_ALL = -1;
-
- /** A user id to indicate the currently active user */
- public static final int USER_CURRENT = -2;
-
- /** A user id constant to indicate the "owner" user of the device */
- public static final int USER_OWNER = 0;
-
- /**
- * 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);
- }
-
- public static final boolean isIsolated(int uid) {
- uid = getAppId(uid);
- return uid >= Process.FIRST_ISOLATED_UID && uid <= Process.LAST_ISOLATED_UID;
- }
-
- public static boolean isApp(int uid) {
- if (uid > 0) {
- uid = UserId.getAppId(uid);
- return uid >= Process.FIRST_APPLICATION_UID && uid <= Process.LAST_APPLICATION_UID;
- } else {
- return false;
- }
- }
-
- /**
- * 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;
- }
- }
-
- public static final int getCallingUserId() {
- return getUserId(Binder.getCallingUid());
- }
-
- /**
- * 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;
- }
-
- /**
- * Returns the user id of the current process
- * @return user id of the current process
- */
- public static final int myUserId() {
- return getUserId(Process.myUid());
- }
-}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 9c73392..0338ee7 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -51,7 +51,7 @@
* @hide
* */
public int getUserHandle() {
- return Process.myUserHandle();
+ return UserHandle.myUserId();
}
/**
@@ -209,4 +209,40 @@
public int getMaxSupportedUsers() {
return mContext.getResources().getInteger(R.integer.config_multiuserMaximumUsers);
}
+
+ /**
+ * Returns a serial number on this device for a given userHandle. User handles can be recycled
+ * when deleting and creating users, but serial numbers are not reused until the device is wiped.
+ * @param userHandle
+ * @return a serial number associated with that user, or -1 if the userHandle is not valid.
+ * @hide
+ */
+ public int getUserSerialNumber(int userHandle) {
+ try {
+ return mService.getUserSerialNumber(userHandle);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not get serial number for user " + userHandle);
+ }
+ return -1;
+ }
+
+ /**
+ * Returns a userHandle on this device for a given user serial number. User handles can be
+ * recycled when deleting and creating users, but serial numbers are not reused until the device
+ * is wiped.
+ * @param userSerialNumber
+ * @return the userHandle associated with that user serial number, or -1 if the serial number
+ * is not valid.
+ * @hide
+ */
+ public int getUserHandle(int userSerialNumber) {
+ try {
+ return mService.getUserHandle(userSerialNumber);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not get userHandle for user " + userSerialNumber);
+ }
+ return -1;
+ }
+
+
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e08ec1f..6dbba46 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -41,7 +41,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
-import android.os.UserId;
+import android.os.UserHandle;
import android.speech.tts.TextToSpeech;
import android.text.TextUtils;
import android.util.AndroidException;
@@ -2336,7 +2336,7 @@
if (sLockSettings != null && !sIsSystemProcess
&& MOVED_TO_LOCK_SETTINGS.contains(name)) {
try {
- return sLockSettings.getString(name, "0", UserId.getCallingUserId());
+ return sLockSettings.getString(name, "0", UserHandle.getCallingUserId());
} catch (RemoteException re) {
// Fall through
}
diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java
index b4f5e12..df85b2f 100644
--- a/core/java/android/server/search/SearchManagerService.java
+++ b/core/java/android/server/search/SearchManagerService.java
@@ -37,7 +37,7 @@
import android.os.Binder;
import android.os.Process;
import android.os.RemoteException;
-import android.os.UserId;
+import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.util.Log;
@@ -186,45 +186,45 @@
Log.e(TAG, "getSearchableInfo(), activity == null");
return null;
}
- return getSearchables(UserId.getCallingUserId()).getSearchableInfo(launchActivity);
+ return getSearchables(UserHandle.getCallingUserId()).getSearchableInfo(launchActivity);
}
/**
* Returns a list of the searchable activities that can be included in global search.
*/
public List<SearchableInfo> getSearchablesInGlobalSearch() {
- return getSearchables(UserId.getCallingUserId()).getSearchablesInGlobalSearchList();
+ return getSearchables(UserHandle.getCallingUserId()).getSearchablesInGlobalSearchList();
}
public List<ResolveInfo> getGlobalSearchActivities() {
- return getSearchables(UserId.getCallingUserId()).getGlobalSearchActivities();
+ return getSearchables(UserHandle.getCallingUserId()).getGlobalSearchActivities();
}
/**
* Gets the name of the global search activity.
*/
public ComponentName getGlobalSearchActivity() {
- return getSearchables(UserId.getCallingUserId()).getGlobalSearchActivity();
+ return getSearchables(UserHandle.getCallingUserId()).getGlobalSearchActivity();
}
/**
* Gets the name of the web search activity.
*/
public ComponentName getWebSearchActivity() {
- return getSearchables(UserId.getCallingUserId()).getWebSearchActivity();
+ return getSearchables(UserHandle.getCallingUserId()).getWebSearchActivity();
}
@Override
public ComponentName getAssistIntent(int userHandle) {
try {
- if (userHandle != UserId.getCallingUserId()) {
+ if (userHandle != UserHandle.getCallingUserId()) {
// Requesting a different user, make sure that they have the permission
if (ActivityManager.checkComponentPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
Binder.getCallingUid(), -1, true)
== PackageManager.PERMISSION_GRANTED) {
// Translate to the current user id, if caller wasn't aware
- if (userHandle == UserId.USER_CURRENT) {
+ if (userHandle == UserHandle.USER_CURRENT) {
long identity = Binder.clearCallingIdentity();
userHandle = ActivityManagerNative.getDefault().getCurrentUser().id;
Binder.restoreCallingIdentity(identity);
@@ -232,7 +232,7 @@
} else {
String msg = "Permission Denial: "
+ "Request to getAssistIntent for " + userHandle
- + " but is calling from user " + UserId.getCallingUserId()
+ + " but is calling from user " + UserHandle.getCallingUserId()
+ "; this requires "
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
Slog.w(TAG, msg);
diff --git a/core/java/android/util/FloatMath.java b/core/java/android/util/FloatMath.java
index 1d4eda4..e05169a 100644
--- a/core/java/android/util/FloatMath.java
+++ b/core/java/android/util/FloatMath.java
@@ -80,4 +80,14 @@
* @return the exponential of value
*/
public static native float exp(float value);
+
+ /**
+ * Returns {@code sqrt(}<i>{@code x}</i><sup>{@code 2}</sup>{@code +} <i>
+ * {@code y}</i><sup>{@code 2}</sup>{@code )}.
+ *
+ * @param x a float number
+ * @param y a float number
+ * @return the hypotenuse
+ */
+ public static native float hypot(float x, float y);
}
diff --git a/core/java/android/util/Spline.java b/core/java/android/util/Spline.java
new file mode 100644
index 0000000..ed027eb
--- /dev/null
+++ b/core/java/android/util/Spline.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+/**
+ * Performs spline interpolation given a set of control points.
+ * @hide
+ */
+public final class Spline {
+ private final float[] mX;
+ private final float[] mY;
+ private final float[] mM;
+
+ private Spline(float[] x, float[] y, float[] m) {
+ mX = x;
+ mY = y;
+ mM = m;
+ }
+
+ /**
+ * Creates a monotone cubic spline from a given set of control points.
+ *
+ * The spline is guaranteed to pass through each control point exactly.
+ * Moreover, assuming the control points are monotonic (Y is non-decreasing or
+ * non-increasing) then the interpolated values will also be monotonic.
+ *
+ * This function uses the Fritsch-Carlson method for computing the spline parameters.
+ * http://en.wikipedia.org/wiki/Monotone_cubic_interpolation
+ *
+ * @param x The X component of the control points, strictly increasing.
+ * @param y The Y component of the control points, monotonic.
+ * @return
+ *
+ * @throws IllegalArgumentException if the X or Y arrays are null, have
+ * different lengths or have fewer than 2 values.
+ * @throws IllegalArgumentException if the control points are not monotonic.
+ */
+ public static Spline createMonotoneCubicSpline(float[] x, float[] y) {
+ if (x == null || y == null || x.length != y.length || x.length < 2) {
+ throw new IllegalArgumentException("There must be at least two control "
+ + "points and the arrays must be of equal length.");
+ }
+
+ final int n = x.length;
+ float[] d = new float[n - 1]; // could optimize this out
+ float[] m = new float[n];
+
+ // Compute slopes of secant lines between successive points.
+ for (int i = 0; i < n - 1; i++) {
+ float h = x[i + 1] - x[i];
+ if (h <= 0f) {
+ throw new IllegalArgumentException("The control points must all "
+ + "have strictly increasing X values.");
+ }
+ d[i] = (y[i + 1] - y[i]) / h;
+ }
+
+ // Initialize the tangents as the average of the secants.
+ m[0] = d[0];
+ for (int i = 1; i < n - 1; i++) {
+ m[i] = (d[i - 1] + d[i]) * 0.5f;
+ }
+ m[n - 1] = d[n - 2];
+
+ // Update the tangents to preserve monotonicity.
+ for (int i = 0; i < n - 1; i++) {
+ if (d[i] == 0f) { // successive Y values are equal
+ m[i] = 0f;
+ m[i + 1] = 0f;
+ } else {
+ float a = m[i] / d[i];
+ float b = m[i + 1] / d[i];
+ if (a < 0f || b < 0f) {
+ throw new IllegalArgumentException("The control points must have "
+ + "monotonic Y values.");
+ }
+ float h = FloatMath.hypot(a, b);
+ if (h > 9f) {
+ float t = 3f / h;
+ m[i] = t * a * d[i];
+ m[i + 1] = t * b * d[i];
+ }
+ }
+ }
+ return new Spline(x, y, m);
+ }
+
+ /**
+ * Interpolates the value of Y = f(X) for given X.
+ * Clamps X to the domain of the spline.
+ *
+ * @param x The X value.
+ * @return The interpolated Y = f(X) value.
+ */
+ public float interpolate(float x) {
+ // Handle the boundary cases.
+ final int n = mX.length;
+ if (Float.isNaN(x)) {
+ return x;
+ }
+ if (x <= mX[0]) {
+ return mY[0];
+ }
+ if (x >= mX[n - 1]) {
+ return mY[n - 1];
+ }
+
+ // Find the index 'i' of the last point with smaller X.
+ // We know this will be within the spline due to the boundary tests.
+ int i = 0;
+ while (x >= mX[i + 1]) {
+ i += 1;
+ if (x == mX[i]) {
+ return mY[i];
+ }
+ }
+
+ // Perform cubic Hermite spline interpolation.
+ float h = mX[i + 1] - mX[i];
+ float t = (x - mX[i]) / h;
+ return (mY[i] * (1 + 2 * t) + h * mM[i] * t) * (1 - t) * (1 - t)
+ + (mY[i + 1] * (3 - 2 * t) + h * mM[i + 1] * (t - 1)) * t * t;
+ }
+
+ // For debugging.
+ @Override
+ public String toString() {
+ StringBuilder str = new StringBuilder();
+ final int n = mX.length;
+ str.append("[");
+ for (int i = 0; i < n; i++) {
+ if (i != 0) {
+ str.append(", ");
+ }
+ str.append("(").append(mX[i]);
+ str.append(", ").append(mY[i]);
+ str.append(": ").append(mM[i]).append(")");
+ }
+ str.append("]");
+ return str.toString();
+ }
+}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 59aeffc..2f2c906 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2790,6 +2790,20 @@
int mUserPaddingEnd;
/**
+ * Whether a left padding has been defined during layout inflation.
+ *
+ * @hide
+ */
+ boolean mUserPaddingLeftDefined = false;
+
+ /**
+ * Whether a right padding has been defined during layout inflation.
+ *
+ * @hide
+ */
+ boolean mUserPaddingRightDefined = false;
+
+ /**
* Default undefined padding
*/
private static final int UNDEFINED_PADDING = Integer.MIN_VALUE;
@@ -3195,8 +3209,11 @@
boolean transformSet = false;
int scrollbarStyle = SCROLLBARS_INSIDE_OVERLAY;
-
int overScrollMode = mOverScrollMode;
+ boolean initializeScrollbars = false;
+
+ final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
+
final int N = a.getIndexCount();
for (int i = 0; i < N; i++) {
int attr = a.getIndex(i);
@@ -3206,15 +3223,19 @@
break;
case com.android.internal.R.styleable.View_padding:
padding = a.getDimensionPixelSize(attr, -1);
+ mUserPaddingLeftDefined = true;
+ mUserPaddingRightDefined = true;
break;
case com.android.internal.R.styleable.View_paddingLeft:
leftPadding = a.getDimensionPixelSize(attr, -1);
+ mUserPaddingLeftDefined = true;
break;
case com.android.internal.R.styleable.View_paddingTop:
topPadding = a.getDimensionPixelSize(attr, -1);
break;
case com.android.internal.R.styleable.View_paddingRight:
rightPadding = a.getDimensionPixelSize(attr, -1);
+ mUserPaddingRightDefined = true;
break;
case com.android.internal.R.styleable.View_paddingBottom:
bottomPadding = a.getDimensionPixelSize(attr, -1);
@@ -3359,12 +3380,12 @@
if (scrollbars != SCROLLBARS_NONE) {
viewFlagValues |= scrollbars;
viewFlagMasks |= SCROLLBARS_MASK;
- initializeScrollbars(a);
+ initializeScrollbars = true;
}
break;
//noinspection deprecation
case R.styleable.View_fadingEdge:
- if (context.getApplicationInfo().targetSdkVersion >= ICE_CREAM_SANDWICH) {
+ if (targetSdkVersion >= ICE_CREAM_SANDWICH) {
// Ignore the attribute starting with ICS
break;
}
@@ -3496,12 +3517,11 @@
}
}
- a.recycle();
-
setOverScrollMode(overScrollMode);
- // Cache user padding as we cannot fully resolve padding here (we dont have yet the resolved
- // layout direction). Those cached values will be used later during padding resolution.
+ // Cache start/end user padding as we cannot fully resolve padding here (we dont have yet
+ // the resolved layout direction). Those cached values will be used later during padding
+ // resolution.
mUserPaddingStart = startPadding;
mUserPaddingEnd = endPadding;
@@ -3529,6 +3549,12 @@
setFlags(viewFlagValues, viewFlagMasks);
}
+ if (initializeScrollbars) {
+ initializeScrollbars(a);
+ }
+
+ a.recycle();
+
// Needs to be called after mViewFlags is set
if (scrollbarStyle != SCROLLBARS_INSIDE_OVERLAY) {
recomputePadding();
@@ -5533,10 +5559,13 @@
// Reset the current layout direction and the resolved one
mPrivateFlags2 &= ~LAYOUT_DIRECTION_MASK;
resetResolvedLayoutDirection();
- // Set the new layout direction (filtered) and ask for a layout pass
+ // Reset padding resolution
+ mPrivateFlags2 &= ~PADDING_RESOLVED;
+ // Set the new layout direction (filtered)
mPrivateFlags2 |=
((layoutDirection << LAYOUT_DIRECTION_MASK_SHIFT) & LAYOUT_DIRECTION_MASK);
- resolvePadding();
+ resolveRtlProperties();
+ // ... and ask for a layout pass
requestLayout();
}
}
@@ -5552,6 +5581,11 @@
@ViewDebug.IntToString(from = LAYOUT_DIRECTION_RTL, to = "RESOLVED_DIRECTION_RTL")
})
public int getResolvedLayoutDirection() {
+ final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
+ if (targetSdkVersion < JELLY_BEAN_MR1) {
+ mPrivateFlags2 |= LAYOUT_DIRECTION_RESOLVED;
+ return LAYOUT_DIRECTION_LTR;
+ }
// The layout direction will be resolved only if needed
if ((mPrivateFlags2 & LAYOUT_DIRECTION_RESOLVED) != LAYOUT_DIRECTION_RESOLVED) {
resolveLayoutDirection();
@@ -9657,6 +9691,7 @@
throw new NullPointerException("Layout parameters cannot be null");
}
mLayoutParams = params;
+ resolveLayoutParams();
if (mParent instanceof ViewGroup) {
((ViewGroup) mParent).onSetLayoutParams(this, params);
}
@@ -9664,6 +9699,15 @@
}
/**
+ * Resolve the layout parameters depending on the resolved layout direction
+ */
+ private void resolveLayoutParams() {
+ if (mLayoutParams != null) {
+ mLayoutParams.onResolveLayoutDirection(getResolvedLayoutDirection());
+ }
+ }
+
+ /**
* Set the scrolled position of your view. This will cause a call to
* {@link #onScrollChanged(int, int, int, int)} and the view will be
* invalidated.
@@ -11197,12 +11241,7 @@
jumpDrawablesToCurrentState();
- // Order is important here: LayoutDirection MUST be resolved before Padding
- // and TextDirection
- resolveLayoutDirection();
- resolvePadding();
- resolveTextDirection();
- resolveTextAlignment();
+ resolveRtlProperties();
clearAccessibilityFocus();
if (isFocused()) {
@@ -11215,6 +11254,16 @@
}
}
+ void resolveRtlProperties() {
+ // Order is important here: LayoutDirection MUST be resolved first...
+ resolveLayoutDirection();
+ // ... then we can resolve the others properties depending on the resolved LayoutDirection.
+ resolvePadding();
+ resolveLayoutParams();
+ resolveTextDirection();
+ resolveTextAlignment();
+ }
+
/**
* @see #onScreenStateChanged(int)
*/
@@ -11295,55 +11344,65 @@
}
/**
+ * Return if padding has been resolved
+ */
+ boolean isPaddingResolved() {
+ return (mPrivateFlags2 & PADDING_RESOLVED) != 0;
+ }
+
+ /**
* Resolve padding depending on layout direction.
*/
public void resolvePadding() {
- // If the user specified the absolute padding (either with android:padding or
- // android:paddingLeft/Top/Right/Bottom), use this padding, otherwise
- // use the default padding or the padding from the background drawable
- // (stored at this point in mPadding*)
- int resolvedLayoutDirection = getResolvedLayoutDirection();
- switch (resolvedLayoutDirection) {
- case LAYOUT_DIRECTION_RTL:
- // Start user padding override Right user padding. Otherwise, if Right user
- // padding is not defined, use the default Right padding. If Right user padding
- // is defined, just use it.
- if (mUserPaddingStart != UNDEFINED_PADDING) {
- mUserPaddingRight = mUserPaddingStart;
- }
- if (mUserPaddingRight == UNDEFINED_PADDING) {
- mUserPaddingRight = mPaddingRight;
- }
- if (mUserPaddingEnd != UNDEFINED_PADDING) {
- mUserPaddingLeft = mUserPaddingEnd;
- }
- if (mUserPaddingLeft == UNDEFINED_PADDING) {
- mUserPaddingLeft = mPaddingLeft;
- }
- break;
- case LAYOUT_DIRECTION_LTR:
- default:
- // Start user padding override Left user padding. Otherwise, if Left user
- // padding is not defined, use the default left padding. If Left user padding
- // is defined, just use it.
- if (mUserPaddingStart != UNDEFINED_PADDING) {
- mUserPaddingLeft = mUserPaddingStart;
- }
- if (mUserPaddingLeft == UNDEFINED_PADDING) {
- mUserPaddingLeft = mPaddingLeft;
- }
- if (mUserPaddingEnd != UNDEFINED_PADDING) {
- mUserPaddingRight = mUserPaddingEnd;
- }
- if (mUserPaddingRight == UNDEFINED_PADDING) {
- mUserPaddingRight = mPaddingRight;
- }
+ final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
+ if (targetSdkVersion < JELLY_BEAN_MR1 || !hasRtlSupport()) {
+ // Pre Jelly Bean MR1 case (compatibility mode) OR no RTL support case:
+ // left / right padding are used if defined. If they are not defined and start / end
+ // padding are defined (e.g. in Frameworks resources), then we use start / end and
+ // resolve them as left / right (layout direction is not taken into account).
+ if (!mUserPaddingLeftDefined && mUserPaddingStart != UNDEFINED_PADDING) {
+ mUserPaddingLeft = mUserPaddingStart;
+ }
+ if (!mUserPaddingRightDefined && mUserPaddingEnd != UNDEFINED_PADDING) {
+ mUserPaddingRight = mUserPaddingEnd;
+ }
+
+ mUserPaddingBottom = (mUserPaddingBottom >= 0) ? mUserPaddingBottom : mPaddingBottom;
+
+ internalSetPadding(mUserPaddingLeft, mPaddingTop, mUserPaddingRight,
+ mUserPaddingBottom);
+ } else {
+ // Post Jelly Bean MR1 case: we need to take the resolved layout direction into account.
+ // If start / end padding are defined, they will be resolved (hence overriding) to
+ // left / right or right / left depending on the resolved layout direction.
+ // If start / end padding are not defined, use the left / right ones.
+ int resolvedLayoutDirection = getResolvedLayoutDirection();
+ switch (resolvedLayoutDirection) {
+ case LAYOUT_DIRECTION_RTL:
+ if (mUserPaddingStart != UNDEFINED_PADDING) {
+ mUserPaddingRight = mUserPaddingStart;
+ }
+ if (mUserPaddingEnd != UNDEFINED_PADDING) {
+ mUserPaddingLeft = mUserPaddingEnd;
+ }
+ break;
+ case LAYOUT_DIRECTION_LTR:
+ default:
+ if (mUserPaddingStart != UNDEFINED_PADDING) {
+ mUserPaddingLeft = mUserPaddingStart;
+ }
+ if (mUserPaddingEnd != UNDEFINED_PADDING) {
+ mUserPaddingRight = mUserPaddingEnd;
+ }
+ }
+
+ mUserPaddingBottom = (mUserPaddingBottom >= 0) ? mUserPaddingBottom : mPaddingBottom;
+
+ internalSetPadding(mUserPaddingLeft, mPaddingTop, mUserPaddingRight,
+ mUserPaddingBottom);
+ onPaddingChanged(resolvedLayoutDirection);
}
- mUserPaddingBottom = (mUserPaddingBottom >= 0) ? mUserPaddingBottom : mPaddingBottom;
-
- internalSetPadding(mUserPaddingLeft, mPaddingTop, mUserPaddingRight, mUserPaddingBottom);
- onPaddingChanged(resolvedLayoutDirection);
mPrivateFlags2 |= PADDING_RESOLVED;
}
@@ -14016,6 +14075,8 @@
}
background.setLayoutDirection(getResolvedLayoutDirection());
if (background.getPadding(padding)) {
+ // Reset padding resolution
+ mPrivateFlags2 &= ~PADDING_RESOLVED;
switch (background.getLayoutDirection()) {
case LAYOUT_DIRECTION_RTL:
internalSetPadding(padding.right, padding.top, padding.left, padding.bottom);
@@ -14112,13 +14173,19 @@
* @param bottom the bottom padding in pixels
*/
public void setPadding(int left, int top, int right, int bottom) {
+ // Reset padding resolution
+ mPrivateFlags2 &= ~PADDING_RESOLVED;
+
mUserPaddingStart = UNDEFINED_PADDING;
mUserPaddingEnd = UNDEFINED_PADDING;
internalSetPadding(left, top, right, bottom);
}
- void internalSetPadding(int left, int top, int right, int bottom) {
+ /**
+ * @hide
+ */
+ protected void internalSetPadding(int left, int top, int right, int bottom) {
mUserPaddingLeft = left;
mUserPaddingRight = right;
mUserPaddingBottom = bottom;
@@ -14193,6 +14260,9 @@
* @param bottom the bottom padding in pixels
*/
public void setPaddingRelative(int start, int top, int end, int bottom) {
+ // Reset padding resolution
+ mPrivateFlags2 &= ~PADDING_RESOLVED;
+
mUserPaddingStart = start;
mUserPaddingEnd = end;
@@ -14234,6 +14304,9 @@
* @return the left padding in pixels
*/
public int getPaddingLeft() {
+ if (!isPaddingResolved()) {
+ resolvePadding();
+ }
return mPaddingLeft;
}
@@ -14245,6 +14318,9 @@
* @return the start padding in pixels
*/
public int getPaddingStart() {
+ if (!isPaddingResolved()) {
+ resolvePadding();
+ }
return (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ?
mPaddingRight : mPaddingLeft;
}
@@ -14257,6 +14333,9 @@
* @return the right padding in pixels
*/
public int getPaddingRight() {
+ if (!isPaddingResolved()) {
+ resolvePadding();
+ }
return mPaddingRight;
}
@@ -14268,6 +14347,9 @@
* @return the end padding in pixels
*/
public int getPaddingEnd() {
+ if (!isPaddingResolved()) {
+ resolvePadding();
+ }
return (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ?
mPaddingLeft : mPaddingRight;
}
@@ -14913,10 +14995,6 @@
mPrivateFlags |= FORCE_LAYOUT;
mPrivateFlags |= INVALIDATED;
- if (mLayoutParams != null) {
- mLayoutParams.onResolveLayoutDirection(getResolvedLayoutDirection());
- }
-
if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout();
}
@@ -14960,7 +15038,7 @@
// first clears the measured dimension flag
mPrivateFlags &= ~MEASURED_DIMENSION_SET;
- if ((mPrivateFlags2 & PADDING_RESOLVED) == 0) {
+ if (!isPaddingResolved()) {
resolvePadding();
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 1548743..bfe0ca5 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2517,8 +2517,11 @@
super.dispatchDetachedFromWindow();
}
+ /**
+ * @hide
+ */
@Override
- void internalSetPadding(int left, int top, int right, int bottom) {
+ protected void internalSetPadding(int left, int top, int right, int bottom) {
super.internalSetPadding(left, top, right, bottom);
if ((mPaddingLeft | mPaddingTop | mPaddingRight | mPaddingBottom) != 0) {
@@ -3372,6 +3375,11 @@
if (child.hasTransientState()) {
childHasTransientStateChanged(child, true);
}
+
+ if (child.getLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT) {
+ child.resetResolvedLayoutDirection();
+ child.resolveRtlProperties();
+ }
}
private void addInArray(View child, int index) {
@@ -3597,6 +3605,8 @@
childHasTransientStateChanged(view, false);
}
+ view.resetResolvedLayoutDirection();
+
onViewRemoved(view);
needGlobalAttributesUpdate(false);
diff --git a/core/java/android/webkit/DeviceOrientationService.java b/core/java/android/webkit/DeviceOrientationService.java
index 2e8656c..a4d240d 100755
--- a/core/java/android/webkit/DeviceOrientationService.java
+++ b/core/java/android/webkit/DeviceOrientationService.java
@@ -123,7 +123,7 @@
// The angles are in radians
float[] rotationAngles = new float[3];
SensorManager.getOrientation(deviceRotationMatrix, rotationAngles);
- double alpha = Math.toDegrees(-rotationAngles[0]) - 90.0;
+ double alpha = Math.toDegrees(-rotationAngles[0]);
while (alpha < 0.0) { alpha += 360.0; } // [0, 360)
double beta = Math.toDegrees(-rotationAngles[1]);
while (beta < -180.0) { beta += 360.0; } // [-180, 180)
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index 1acbab1..8d79492 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -16,6 +16,7 @@
package android.webkit;
+import android.accessibilityservice.AccessibilityServiceInfo;
import android.animation.ObjectAnimator;
import android.annotation.Widget;
import android.app.ActivityManager;
@@ -1738,8 +1739,21 @@
event.setMaxScrollY(Math.max(convertedContentHeight - adjustedViewHeight, 0));
}
- private boolean isAccessibilityEnabled() {
- return AccessibilityManager.getInstance(mContext).isEnabled();
+ private boolean isAccessibilityInjectionEnabled() {
+ final AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
+ if (!manager.isEnabled()) {
+ return false;
+ }
+
+ // Accessibility scripts should be injected only when a speaking service
+ // is enabled. This may need to change later to accommodate Braille.
+ final List<AccessibilityServiceInfo> services = manager.getEnabledAccessibilityServiceList(
+ AccessibilityServiceInfo.FEEDBACK_SPOKEN);
+ if (services.isEmpty()) {
+ return false;
+ }
+
+ return true;
}
private AccessibilityInjector getAccessibilityInjector() {
@@ -3925,7 +3939,7 @@
// reset the flag since we set to true in if need after
// loading is see onPageFinished(Url)
- if (isAccessibilityEnabled()) {
+ if (isAccessibilityInjectionEnabled()) {
getAccessibilityInjector().onPageStarted(url);
}
@@ -3940,7 +3954,7 @@
/* package */ void onPageFinished(String url) {
mZoomManager.onPageFinished(url);
- if (isAccessibilityEnabled()) {
+ if (isAccessibilityInjectionEnabled()) {
getAccessibilityInjector().onPageFinished(url);
}
}
@@ -4981,7 +4995,7 @@
}
// See if the accessibility injector needs to handle this event.
- if (isAccessibilityEnabled()
+ if (isAccessibilityInjectionEnabled()
&& getAccessibilityInjector().handleKeyEventIfNecessary(event)) {
return true;
}
@@ -5088,7 +5102,7 @@
}
// See if the accessibility injector needs to handle this event.
- if (isAccessibilityEnabled()
+ if (isAccessibilityInjectionEnabled()
&& getAccessibilityInjector().handleKeyEventIfNecessary(event)) {
return true;
}
@@ -5339,7 +5353,7 @@
public void onAttachedToWindow() {
if (mWebView.hasWindowFocus()) setActive(true);
- if (isAccessibilityEnabled()) {
+ if (isAccessibilityInjectionEnabled()) {
getAccessibilityInjector().addAccessibilityApisIfNecessary();
}
@@ -5352,7 +5366,7 @@
mZoomManager.dismissZoomPicker();
if (mWebView.hasWindowFocus()) setActive(false);
- if (isAccessibilityEnabled()) {
+ if (isAccessibilityInjectionEnabled()) {
getAccessibilityInjector().removeAccessibilityApisIfNecessary();
} else {
// Ensure the injector is cleared if we're detaching from the window
@@ -7434,7 +7448,7 @@
break;
case SELECTION_STRING_CHANGED:
- if (isAccessibilityEnabled()) {
+ if (isAccessibilityInjectionEnabled()) {
getAccessibilityInjector()
.handleSelectionChangedIfNecessary((String) msg.obj);
}
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index 46079f9..f91201a 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -151,7 +151,7 @@
mCheckMarkWidth = 0;
}
mCheckMarkDrawable = d;
- // Do padding resolution. This will call setPadding() and do a requestLayout() if needed.
+ // Do padding resolution. This will call internalSetPadding() and do a requestLayout() if needed.
resolvePadding();
}
@@ -169,6 +169,19 @@
return mCheckMarkDrawable;
}
+ /**
+ * @hide
+ */
+ @Override
+ protected void internalSetPadding(int left, int top, int right, int bottom) {
+ super.internalSetPadding(left, top, right, bottom);
+ if (isLayoutRtl()) {
+ mBasePadding = mUserPaddingLeft;
+ } else {
+ mBasePadding = mUserPaddingRight;
+ }
+ }
+
@Override
public void onPaddingChanged(int layoutDirection) {
int newPadding = (mCheckMarkDrawable != null) ?
@@ -221,8 +234,15 @@
final int width = getWidth();
final int top = y;
final int bottom = top + height;
- final int left = isLayoutRtl ? getPaddingEnd() : width - getPaddingEnd();
- final int right = left + mCheckMarkWidth;
+ final int left;
+ final int right;
+ if (isLayoutRtl) {
+ right = getPaddingEnd();
+ left = right - mCheckMarkWidth;
+ } else {
+ left = width - getPaddingEnd();
+ right = left + mCheckMarkWidth;
+ }
checkMarkDrawable.setBounds( left, top, right, bottom);
checkMarkDrawable.draw(canvas);
}
diff --git a/core/java/android/widget/TabHost.java b/core/java/android/widget/TabHost.java
index 8bb9348..238dc55 100644
--- a/core/java/android/widget/TabHost.java
+++ b/core/java/android/widget/TabHost.java
@@ -48,6 +48,10 @@
*/
public class TabHost extends FrameLayout implements ViewTreeObserver.OnTouchModeChangeListener {
+ private static final int TABWIDGET_LOCATION_LEFT = 0;
+ private static final int TABWIDGET_LOCATION_TOP = 1;
+ private static final int TABWIDGET_LOCATION_RIGHT = 2;
+ private static final int TABWIDGET_LOCATION_BOTTOM = 3;
private TabWidget mTabWidget;
private FrameLayout mTabContent;
private List<TabSpec> mTabSpecs = new ArrayList<TabSpec>(2);
@@ -293,22 +297,73 @@
return mTabContent;
}
+ /**
+ * Get the location of the TabWidget.
+ *
+ * @return The TabWidget location.
+ */
+ private int getTabWidgetLocation() {
+ int location = TABWIDGET_LOCATION_TOP;
+
+ switch (mTabWidget.getOrientation()) {
+ case LinearLayout.VERTICAL:
+ location = (mTabContent.getLeft() < mTabWidget.getLeft()) ? TABWIDGET_LOCATION_RIGHT
+ : TABWIDGET_LOCATION_LEFT;
+ break;
+ case LinearLayout.HORIZONTAL:
+ default:
+ location = (mTabContent.getTop() < mTabWidget.getTop()) ? TABWIDGET_LOCATION_BOTTOM
+ : TABWIDGET_LOCATION_TOP;
+ break;
+ }
+ return location;
+ }
+
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
final boolean handled = super.dispatchKeyEvent(event);
- // unhandled key ups change focus to tab indicator for embedded activities
- // when there is nothing that will take focus from default focus searching
+ // unhandled key events change focus to tab indicator for embedded
+ // activities when there is nothing that will take focus from default
+ // focus searching
if (!handled
&& (event.getAction() == KeyEvent.ACTION_DOWN)
- && (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_UP)
&& (mCurrentView != null)
&& (mCurrentView.isRootNamespace())
- && (mCurrentView.hasFocus())
- && (mCurrentView.findFocus().focusSearch(View.FOCUS_UP) == null)) {
- mTabWidget.getChildTabViewAt(mCurrentTab).requestFocus();
- playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
- return true;
+ && (mCurrentView.hasFocus())) {
+ int keyCodeShouldChangeFocus = KeyEvent.KEYCODE_DPAD_UP;
+ int directionShouldChangeFocus = View.FOCUS_UP;
+ int soundEffect = SoundEffectConstants.NAVIGATION_UP;
+
+ switch (getTabWidgetLocation()) {
+ case TABWIDGET_LOCATION_LEFT:
+ keyCodeShouldChangeFocus = KeyEvent.KEYCODE_DPAD_LEFT;
+ directionShouldChangeFocus = View.FOCUS_LEFT;
+ soundEffect = SoundEffectConstants.NAVIGATION_LEFT;
+ break;
+ case TABWIDGET_LOCATION_RIGHT:
+ keyCodeShouldChangeFocus = KeyEvent.KEYCODE_DPAD_RIGHT;
+ directionShouldChangeFocus = View.FOCUS_RIGHT;
+ soundEffect = SoundEffectConstants.NAVIGATION_RIGHT;
+ break;
+ case TABWIDGET_LOCATION_BOTTOM:
+ keyCodeShouldChangeFocus = KeyEvent.KEYCODE_DPAD_DOWN;
+ directionShouldChangeFocus = View.FOCUS_DOWN;
+ soundEffect = SoundEffectConstants.NAVIGATION_DOWN;
+ break;
+ case TABWIDGET_LOCATION_TOP:
+ default:
+ keyCodeShouldChangeFocus = KeyEvent.KEYCODE_DPAD_UP;
+ directionShouldChangeFocus = View.FOCUS_UP;
+ soundEffect = SoundEffectConstants.NAVIGATION_UP;
+ break;
+ }
+ if (event.getKeyCode() == keyCodeShouldChangeFocus
+ && mCurrentView.findFocus().focusSearch(directionShouldChangeFocus) == null) {
+ mTabWidget.getChildTabViewAt(mCurrentTab).requestFocus();
+ playSoundEffect(soundEffect);
+ return true;
+ }
}
return handled;
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 84fe8ce..e63c57f 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -37,7 +37,7 @@
import android.os.PatternMatcher;
import android.os.Process;
import android.os.RemoteException;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -133,7 +133,7 @@
mAdapter = new ResolveListAdapter(this, intent, initialIntents, rList,
mLaunchedFromUid);
int count = mAdapter.getCount();
- if (mLaunchedFromUid < 0 || UserId.isIsolated(mLaunchedFromUid)) {
+ if (mLaunchedFromUid < 0 || UserHandle.isIsolated(mLaunchedFromUid)) {
// Gulp!
finish();
return;
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index b016e99..1e268c4 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -18,16 +18,14 @@
import android.net.Credentials;
import android.net.LocalSocket;
-import android.os.Build;
import android.os.Process;
+import android.os.SELinux;
import android.os.SystemProperties;
import android.util.Log;
import dalvik.system.PathClassLoader;
import dalvik.system.Zygote;
-import android.os.SELinux;
-
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -234,9 +232,9 @@
ZygoteInit.setCloseOnExec(serverPipeFd, true);
}
- pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid,
- parsedArgs.gids, parsedArgs.debugFlags, rlimits,
- parsedArgs.seInfo, parsedArgs.niceName);
+ pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
+ parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
+ parsedArgs.niceName);
} catch (IOException ex) {
logAndPrintError(newStderr, "Exception creating pipe", ex);
} catch (ErrnoException ex) {
@@ -341,6 +339,9 @@
*/
int debugFlags;
+ /** From --mount-external */
+ int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
+
/** from --target-sdk-version. */
int targetSdkVersion;
boolean targetSdkVersionSpecified;
@@ -526,6 +527,10 @@
"Duplicate arg specified");
}
niceName = arg.substring(arg.indexOf('=') + 1);
+ } else if (arg.equals("--mount-external-singleuser")) {
+ mountExternal = Zygote.MOUNT_EXTERNAL_SINGLEUSER;
+ } else if (arg.equals("--mount-external-multiuser")) {
+ mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER;
} else {
break;
}
diff --git a/core/java/com/android/internal/statusbar/StatusBarNotification.java b/core/java/com/android/internal/statusbar/StatusBarNotification.java
index d443523..cb87ac4 100644
--- a/core/java/com/android/internal/statusbar/StatusBarNotification.java
+++ b/core/java/com/android/internal/statusbar/StatusBarNotification.java
@@ -19,6 +19,7 @@
import android.app.Notification;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.UserHandle;
import android.widget.RemoteViews;
@@ -132,6 +133,11 @@
return ((notification.flags & Notification.FLAG_ONGOING_EVENT) == 0)
&& ((notification.flags & Notification.FLAG_NO_CLEAR) == 0);
}
+
+ /** Returns a userHandle for the instance of the app that posted this notification. */
+ public int getUserId() {
+ return UserHandle.getUserId(this.uid);
+ }
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index f77e8f3..4777c16 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -34,7 +34,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
import android.os.storage.IMountService;
import android.provider.Settings;
import android.security.KeyStore;
@@ -246,7 +246,7 @@
if (callingUid == android.os.Process.SYSTEM_UID) {
return mCurrentUserId;
} else {
- return UserId.getUserId(callingUid);
+ return UserHandle.getUserId(callingUid);
}
}
diff --git a/core/java/com/android/internal/widget/LockSettingsService.java b/core/java/com/android/internal/widget/LockSettingsService.java
index 2fb81ac..350e006 100644
--- a/core/java/com/android/internal/widget/LockSettingsService.java
+++ b/core/java/com/android/internal/widget/LockSettingsService.java
@@ -25,7 +25,7 @@
import android.os.Binder;
import android.os.RemoteException;
import android.os.SystemProperties;
-import android.os.UserId;
+import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.text.TextUtils;
@@ -97,7 +97,7 @@
private static final void checkWritePermission(int userId) {
final int callingUid = Binder.getCallingUid();
- if (UserId.getAppId(callingUid) != android.os.Process.SYSTEM_UID) {
+ if (UserHandle.getAppId(callingUid) != android.os.Process.SYSTEM_UID) {
throw new SecurityException("uid=" + callingUid
+ " not authorized to write lock settings");
}
@@ -105,7 +105,7 @@
private static final void checkPasswordReadPermission(int userId) {
final int callingUid = Binder.getCallingUid();
- if (UserId.getAppId(callingUid) != android.os.Process.SYSTEM_UID) {
+ if (UserHandle.getAppId(callingUid) != android.os.Process.SYSTEM_UID) {
throw new SecurityException("uid=" + callingUid
+ " not authorized to read lock password");
}
@@ -113,8 +113,8 @@
private static final void checkReadPermission(int userId) {
final int callingUid = Binder.getCallingUid();
- if (UserId.getAppId(callingUid) != android.os.Process.SYSTEM_UID
- && UserId.getUserId(callingUid) != userId) {
+ if (UserHandle.getAppId(callingUid) != android.os.Process.SYSTEM_UID
+ && UserHandle.getUserId(callingUid) != userId) {
throw new SecurityException("uid=" + callingUid
+ " not authorized to read settings of user " + userId);
}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index b1423ca..f950d3d 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -67,7 +67,6 @@
android_os_ParcelFileDescriptor.cpp \
android_os_Parcel.cpp \
android_os_SELinux.cpp \
- android_os_StatFs.cpp \
android_os_SystemClock.cpp \
android_os_SystemProperties.cpp \
android_os_Trace.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index c936b0b..0c88297 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -134,7 +134,6 @@
extern int register_android_os_Parcel(JNIEnv* env);
extern int register_android_os_ParcelFileDescriptor(JNIEnv *env);
extern int register_android_os_SELinux(JNIEnv* env);
-extern int register_android_os_StatFs(JNIEnv *env);
extern int register_android_os_SystemProperties(JNIEnv *env);
extern int register_android_os_SystemClock(JNIEnv* env);
extern int register_android_os_Trace(JNIEnv* env);
@@ -1142,7 +1141,6 @@
REG_JNI(register_android_os_MessageQueue),
REG_JNI(register_android_os_ParcelFileDescriptor),
REG_JNI(register_android_os_SELinux),
- REG_JNI(register_android_os_StatFs),
REG_JNI(register_android_os_Trace),
REG_JNI(register_android_os_UEventObserver),
REG_JNI(register_android_net_LocalSocketImpl),
diff --git a/core/jni/android_os_FileUtils.cpp b/core/jni/android_os_FileUtils.cpp
index 8d65cbc..82bfc36 100644
--- a/core/jni/android_os_FileUtils.cpp
+++ b/core/jni/android_os_FileUtils.cpp
@@ -33,19 +33,6 @@
namespace android {
-static jfieldID gFileStatusDevFieldID;
-static jfieldID gFileStatusInoFieldID;
-static jfieldID gFileStatusModeFieldID;
-static jfieldID gFileStatusNlinkFieldID;
-static jfieldID gFileStatusUidFieldID;
-static jfieldID gFileStatusGidFieldID;
-static jfieldID gFileStatusSizeFieldID;
-static jfieldID gFileStatusBlksizeFieldID;
-static jfieldID gFileStatusBlocksFieldID;
-static jfieldID gFileStatusAtimeFieldID;
-static jfieldID gFileStatusMtimeFieldID;
-static jfieldID gFileStatusCtimeFieldID;
-
jint android_os_FileUtils_setPermissions(JNIEnv* env, jobject clazz,
jstring file, jint mode,
jint uid, jint gid)
@@ -68,39 +55,6 @@
return chmod(file8.string(), mode) == 0 ? 0 : errno;
}
-jint android_os_FileUtils_getPermissions(JNIEnv* env, jobject clazz,
- jstring file, jintArray outArray)
-{
- const jchar* str = env->GetStringCritical(file, 0);
- String8 file8;
- if (str) {
- file8 = String8(str, env->GetStringLength(file));
- env->ReleaseStringCritical(file, str);
- }
- if (file8.size() <= 0) {
- return ENOENT;
- }
- struct stat st;
- if (stat(file8.string(), &st) != 0) {
- return errno;
- }
- jint* array = (jint*)env->GetPrimitiveArrayCritical(outArray, 0);
- if (array) {
- int len = env->GetArrayLength(outArray);
- if (len >= 1) {
- array[0] = st.st_mode;
- }
- if (len >= 2) {
- array[1] = st.st_uid;
- }
- if (len >= 3) {
- array[2] = st.st_gid;
- }
- }
- env->ReleasePrimitiveArrayCritical(outArray, array, 0);
- return 0;
-}
-
jint android_os_FileUtils_setUMask(JNIEnv* env, jobject clazz, jint mask)
{
return umask(mask);
@@ -127,63 +81,16 @@
return result;
}
-jboolean android_os_FileUtils_getFileStatus(JNIEnv* env, jobject clazz, jstring path, jobject fileStatus) {
- const char* pathStr = env->GetStringUTFChars(path, NULL);
- jboolean ret = false;
-
- struct stat s;
- int res = stat(pathStr, &s);
- if (res == 0) {
- ret = true;
- if (fileStatus != NULL) {
- env->SetIntField(fileStatus, gFileStatusDevFieldID, s.st_dev);
- env->SetIntField(fileStatus, gFileStatusInoFieldID, s.st_ino);
- env->SetIntField(fileStatus, gFileStatusModeFieldID, s.st_mode);
- env->SetIntField(fileStatus, gFileStatusNlinkFieldID, s.st_nlink);
- env->SetIntField(fileStatus, gFileStatusUidFieldID, s.st_uid);
- env->SetIntField(fileStatus, gFileStatusGidFieldID, s.st_gid);
- env->SetLongField(fileStatus, gFileStatusSizeFieldID, s.st_size);
- env->SetIntField(fileStatus, gFileStatusBlksizeFieldID, s.st_blksize);
- env->SetLongField(fileStatus, gFileStatusBlocksFieldID, s.st_blocks);
- env->SetLongField(fileStatus, gFileStatusAtimeFieldID, s.st_atime);
- env->SetLongField(fileStatus, gFileStatusMtimeFieldID, s.st_mtime);
- env->SetLongField(fileStatus, gFileStatusCtimeFieldID, s.st_ctime);
- }
- }
-
- env->ReleaseStringUTFChars(path, pathStr);
-
- return ret;
-}
-
static const JNINativeMethod methods[] = {
{"setPermissions", "(Ljava/lang/String;III)I", (void*)android_os_FileUtils_setPermissions},
- {"getPermissions", "(Ljava/lang/String;[I)I", (void*)android_os_FileUtils_getPermissions},
{"setUMask", "(I)I", (void*)android_os_FileUtils_setUMask},
{"getFatVolumeId", "(Ljava/lang/String;)I", (void*)android_os_FileUtils_getFatVolumeId},
- {"getFileStatusNative", "(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z", (void*)android_os_FileUtils_getFileStatus},
};
static const char* const kFileUtilsPathName = "android/os/FileUtils";
int register_android_os_FileUtils(JNIEnv* env)
{
- jclass fileStatusClass = env->FindClass("android/os/FileUtils$FileStatus");
- LOG_FATAL_IF(fileStatusClass == NULL, "Unable to find class android.os.FileUtils$FileStatus");
-
- gFileStatusDevFieldID = env->GetFieldID(fileStatusClass, "dev", "I");
- gFileStatusInoFieldID = env->GetFieldID(fileStatusClass, "ino", "I");
- gFileStatusModeFieldID = env->GetFieldID(fileStatusClass, "mode", "I");
- gFileStatusNlinkFieldID = env->GetFieldID(fileStatusClass, "nlink", "I");
- gFileStatusUidFieldID = env->GetFieldID(fileStatusClass, "uid", "I");
- gFileStatusGidFieldID = env->GetFieldID(fileStatusClass, "gid", "I");
- gFileStatusSizeFieldID = env->GetFieldID(fileStatusClass, "size", "J");
- gFileStatusBlksizeFieldID = env->GetFieldID(fileStatusClass, "blksize", "I");
- gFileStatusBlocksFieldID = env->GetFieldID(fileStatusClass, "blocks", "J");
- gFileStatusAtimeFieldID = env->GetFieldID(fileStatusClass, "atime", "J");
- gFileStatusMtimeFieldID = env->GetFieldID(fileStatusClass, "mtime", "J");
- gFileStatusCtimeFieldID = env->GetFieldID(fileStatusClass, "ctime", "J");
-
return AndroidRuntime::registerNativeMethods(
env, kFileUtilsPathName,
methods, NELEM(methods));
diff --git a/core/jni/android_os_SELinux.cpp b/core/jni/android_os_SELinux.cpp
index 40443ff..e813c38 100644
--- a/core/jni/android_os_SELinux.cpp
+++ b/core/jni/android_os_SELinux.cpp
@@ -1,3 +1,19 @@
+/*
+ * 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.
+ */
+
#define LOG_TAG "SELinuxJNI"
#include <utils/Log.h>
@@ -6,6 +22,7 @@
#include "android_runtime/AndroidRuntime.h"
#ifdef HAVE_SELINUX
#include "selinux/selinux.h"
+#include "selinux/android.h"
#endif
#include <errno.h>
@@ -458,6 +475,27 @@
}
/*
+ * Function: native_restorecon
+ * Purpose: restore default SELinux security context
+ * Parameters: pathname: the pathname for the file to be relabeled
+ * Returns: boolean: (true) file label successfully restored, (false) otherwise
+ * Exceptions: none
+ */
+ static jboolean native_restorecon(JNIEnv *env, jobject clazz, jstring pathname) {
+#ifdef HAVE_SELINUX
+ if (isSELinuxDisabled)
+ return true;
+
+ const char *file = const_cast<char *>(env->GetStringUTFChars(pathname, NULL));
+ int ret = selinux_android_restorecon(file);
+ env->ReleaseStringUTFChars(pathname, file);
+ return (ret == 0);
+#else
+ return true;
+#endif
+ }
+
+ /*
* JNI registration.
*/
static JNINativeMethod method_table[] = {
@@ -472,6 +510,7 @@
{ "getPidContext" , "(I)Ljava/lang/String;" , (void*)getPidCon },
{ "isSELinuxEnforced" , "()Z" , (void*)isSELinuxEnforced},
{ "isSELinuxEnabled" , "()Z" , (void*)isSELinuxEnabled },
+ { "native_restorecon" , "(Ljava/lang/String;)Z" , (void*)native_restorecon},
{ "setBooleanValue" , "(Ljava/lang/String;Z)Z" , (void*)setBooleanValue },
{ "setFileContext" , "(Ljava/lang/String;Ljava/lang/String;)Z" , (void*)setFileCon },
{ "setFSCreateContext" , "(Ljava/lang/String;)Z" , (void*)setFSCreateCon },
diff --git a/core/jni/android_os_StatFs.cpp b/core/jni/android_os_StatFs.cpp
deleted file mode 100644
index 79d8fef..0000000
--- a/core/jni/android_os_StatFs.cpp
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright 2007, 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.
- */
-
-#if INCLUDE_SYS_MOUNT_FOR_STATFS
-#include <sys/mount.h>
-#else
-#include <sys/statfs.h>
-#endif
-
-#include <errno.h>
-
-#include "jni.h"
-#include "JNIHelp.h"
-#include "android_runtime/AndroidRuntime.h"
-
-
-namespace android
-{
-
-// ----------------------------------------------------------------------------
-
-struct fields_t {
- jfieldID context;
-};
-static fields_t fields;
-
-// ----------------------------------------------------------------------------
-
-static jint
-android_os_StatFs_getBlockSize(JNIEnv *env, jobject thiz)
-{
- struct statfs *stat = (struct statfs *)env->GetIntField(thiz, fields.context);
- return stat->f_bsize;
-}
-
-static jint
-android_os_StatFs_getBlockCount(JNIEnv *env, jobject thiz)
-{
- struct statfs *stat = (struct statfs *)env->GetIntField(thiz, fields.context);
- return stat->f_blocks;
-}
-
-static jint
-android_os_StatFs_getFreeBlocks(JNIEnv *env, jobject thiz)
-{
- struct statfs *stat = (struct statfs *)env->GetIntField(thiz, fields.context);
- return stat->f_bfree;
-}
-
-static jint
-android_os_StatFs_getAvailableBlocks(JNIEnv *env, jobject thiz)
-{
- struct statfs *stat = (struct statfs *)env->GetIntField(thiz, fields.context);
- return stat->f_bavail;
-}
-
-static void
-android_os_StatFs_native_restat(JNIEnv *env, jobject thiz, jstring path)
-{
- if (path == NULL) {
- jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- return;
- }
-
- // get the object handle
- struct statfs *stat = (struct statfs *)env->GetIntField(thiz, fields.context);
- if (stat == NULL) {
- jniThrowException(env, "java/lang/NoSuchFieldException", NULL);
- return;
- }
-
- const char* pathstr = env->GetStringUTFChars(path, NULL);
- if (pathstr == NULL) {
- jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
- return;
- }
-
- // note that stat will contain the new file data corresponding to
- // pathstr
- if (statfs(pathstr, stat) != 0) {
- ALOGE("statfs %s failed, errno: %d", pathstr, errno);
- delete stat;
- env->SetIntField(thiz, fields.context, 0);
- jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- }
- // Release pathstr
- env->ReleaseStringUTFChars(path, pathstr);
-}
-
-static void
-android_os_StatFs_native_setup(JNIEnv *env, jobject thiz, jstring path)
-{
- if (path == NULL) {
- jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- return;
- }
-
- struct statfs* stat = new struct statfs;
- if (stat == NULL) {
- jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
- return;
- }
- env->SetIntField(thiz, fields.context, (int)stat);
- android_os_StatFs_native_restat(env, thiz, path);
-}
-
-static void
-android_os_StatFs_native_finalize(JNIEnv *env, jobject thiz)
-{
- struct statfs *stat = (struct statfs *)env->GetIntField(thiz, fields.context);
- if (stat != NULL) {
- delete stat;
- env->SetIntField(thiz, fields.context, 0);
- }
-}
-
-// ----------------------------------------------------------------------------
-
-static JNINativeMethod gMethods[] = {
- {"getBlockSize", "()I", (void *)android_os_StatFs_getBlockSize},
- {"getBlockCount", "()I", (void *)android_os_StatFs_getBlockCount},
- {"getFreeBlocks", "()I", (void *)android_os_StatFs_getFreeBlocks},
- {"getAvailableBlocks", "()I", (void *)android_os_StatFs_getAvailableBlocks},
- {"native_setup", "(Ljava/lang/String;)V", (void *)android_os_StatFs_native_setup},
- {"native_finalize", "()V", (void *)android_os_StatFs_native_finalize},
- {"native_restat", "(Ljava/lang/String;)V", (void *)android_os_StatFs_native_restat},
-};
-
-
-int register_android_os_StatFs(JNIEnv *env)
-{
- jclass clazz;
-
- clazz = env->FindClass("android/os/StatFs");
- if (clazz == NULL) {
- ALOGE("Can't find android/os/StatFs");
- return -1;
- }
-
- fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
- if (fields.context == NULL) {
- ALOGE("Can't find StatFs.mNativeContext");
- return -1;
- }
-
- return AndroidRuntime::registerNativeMethods(env,
- "android/os/StatFs", gMethods, NELEM(gMethods));
-}
-
-} // namespace android
diff --git a/core/jni/android_util_FloatMath.cpp b/core/jni/android_util_FloatMath.cpp
index e30756b..529fbe9 100644
--- a/core/jni/android_util_FloatMath.cpp
+++ b/core/jni/android_util_FloatMath.cpp
@@ -29,6 +29,10 @@
static float ExpF(JNIEnv* env, jobject clazz, float x) {
return expf(x);
}
+
+ static float HypotF(JNIEnv* env, jobject clazz, float x, float y) {
+ return hypotf(x, y);
+ }
};
static JNINativeMethod gMathUtilsMethods[] = {
@@ -38,6 +42,7 @@
{"cos", "(F)F", (void*) MathUtilsGlue::CosF},
{"sqrt", "(F)F", (void*) MathUtilsGlue::SqrtF},
{"exp", "(F)F", (void*) MathUtilsGlue::ExpF},
+ {"hypot", "(FF)F", (void*) MathUtilsGlue::HypotF},
};
int register_android_util_FloatMath(JNIEnv* env)
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 195b1ef..89be5cb 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -283,7 +283,7 @@
<!-- @hide -->
<permission android:name="android.permission.BIND_DIRECTORY_SEARCH"
android:permissionGroup="android.permission-group.PERSONAL_INFO"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|system" />
<!-- Allows an application to read the user's call log. -->
<permission android:name="android.permission.READ_CALL_LOG"
@@ -766,7 +766,6 @@
android:protectionLevel="dangerous"
android:label="@string/permlab_getTasks"
android:description="@string/permdesc_getTasks" />
-
<!-- Allows an application to call APIs that allow it to do interactions
across the users on the device, using singleton services and
user-targeted broadcasts. This permission is not available to
@@ -786,6 +785,15 @@
android:label="@string/permlab_interactAcrossUsersFull"
android:description="@string/permdesc_interactAcrossUsersFull" />
+ <!-- Allows an application to call APIs that allow it to query and manage
+ users on the device. This permission is not available to
+ third party applications. -->
+ <permission android:name="android.permission.MANAGE_USERS"
+ android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+ android:protectionLevel="signature"
+ android:label="@string/permlab_manageUsers"
+ android:description="@string/permdesc_manageUsers" />
+
<!-- Allows an application to get full detailed information about
recently running tasks, with full fidelity to the real state.
@hide -->
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 4a9cd5a..c271baa 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -155,7 +155,7 @@
<string name="global_action_lock" msgid="2844945191792119712">"屏幕锁定"</string>
<string name="global_action_power_off" msgid="4471879440839879722">"关机"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"错误报告"</string>
- <string name="bugreport_title" msgid="2667494803742548533">"获取错误报告"</string>
+ <string name="bugreport_title" msgid="2667494803742548533">"提交错误报告"</string>
<string name="bugreport_message" msgid="398447048750350456">"这会收集有关当前设备状态的信息,并以电子邮件的形式进行发送。从开始生成错误报告到准备好发送需要一点时间,请耐心等待。"</string>
<string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"静音模式"</string>
<string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"声音已关闭"</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ee0ff8e..e3c957b 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -533,13 +533,22 @@
<integer name="config_longPressOnHomeBehavior">2</integer>
<!-- Array of light sensor LUX values to define our levels for auto backlight brightness support.
- The N entries of this array define N + 1 zones as follows:
+ The N entries of this array define N + 1 control points as follows:
- Zone 0: 0 <= LUX < array[0]
- Zone 1: array[0] <= LUX < array[1]
+ Point 1: LUX <= 0 (implicit)
+ Point 2: 0 < level[1] == LUX < level[2]
...
- Zone N: array[N - 1] <= LUX < array[N]
- Zone N + 1: array[N] <= LUX < infinity
+ Point N: level[N - 1] == LUX < level[N]
+ Point N + 1: level[N] <= LUX < infinity
+
+ The control points must be strictly increasing. Each control point
+ corresponds to an entry in the brightness backlight values arrays.
+ For example, if LUX == level[1] (first element of the levels array)
+ then the brightness will be determined by value[1] (first element
+ of the brightness values array).
+
+ Spline interpolation is used to determine the auto-brightness
+ backlight values for LUX levels between these control points.
Must be overridden in platform specific overlays -->
<integer-array name="config_autoBrightnessLevels">
@@ -552,6 +561,7 @@
<!-- Array of output values for LCD backlight corresponding to the LUX values
in the config_autoBrightnessLevels array. This array should have size one greater
than the size of the config_autoBrightnessLevels array.
+ The brightness values must be between 0 and 255 and be non-decreasing.
This must be overridden in platform specific overlays -->
<integer-array name="config_autoBrightnessLcdBacklightValues">
</integer-array>
@@ -559,6 +569,7 @@
<!-- Array of output values for button backlight corresponding to the LUX values
in the config_autoBrightnessLevels array. This array should have size one greater
than the size of the config_autoBrightnessLevels array.
+ The brightness values must be between 0 and 255 and be non-decreasing.
This must be overridden in platform specific overlays -->
<integer-array name="config_autoBrightnessButtonBacklightValues">
</integer-array>
@@ -566,6 +577,7 @@
<!-- Array of output values for keyboard backlight corresponding to the LUX values
in the config_autoBrightnessLevels array. This array should have size one greater
than the size of the config_autoBrightnessLevels array.
+ The brightness values must be between 0 and 255 and be non-decreasing.
This must be overridden in platform specific overlays -->
<integer-array name="config_autoBrightnessKeyboardBacklightValues">
</integer-array>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 352c409..620a002 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -567,6 +567,11 @@
<string name="permdesc_interactAcrossUsersFull">Allows all possible interactions across
users.</string>
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to create/remove/query users. [CHAR LIMIT=none] -->
+ <string name="permlab_manageUsers">manage users</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to create/remove/query users. [CHAR LIMIT=NONE] -->
+ <string name="permdesc_manageUsers">Allows apps to manage users on the device, including query, creation and deletion.</string>
+
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=50] -->
<string name="permlab_getDetailedTasks">retrieve details of running apps</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] -->
diff --git a/core/tests/coretests/src/android/content/pm/AppCacheTest.java b/core/tests/coretests/src/android/content/pm/AppCacheTest.java
index 0c31e2d..8d53db9 100755
--- a/core/tests/coretests/src/android/content/pm/AppCacheTest.java
+++ b/core/tests/coretests/src/android/content/pm/AppCacheTest.java
@@ -24,7 +24,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StatFs;
-import android.os.UserId;
+import android.os.UserHandle;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.MediumTest;
@@ -719,7 +719,7 @@
File getDataDir() {
try {
ApplicationInfo appInfo = getPm().getApplicationInfo(mContext.getPackageName(), 0,
- UserId.myUserId());
+ UserHandle.myUserId());
return new File(appInfo.dataDir);
} catch (RemoteException e) {
throw new RuntimeException("Pacakge manager dead", e);
@@ -748,7 +748,7 @@
@LargeTest
public void testClearApplicationUserDataNoObserver() throws Exception {
- getPm().clearApplicationUserData(mContext.getPackageName(), null, UserId.myUserId());
+ getPm().clearApplicationUserData(mContext.getPackageName(), null, UserHandle.myUserId());
//sleep for 1 minute
Thread.sleep(60*1000);
//confirm files dont exist
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index f12cbe1..4d0b892 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -17,21 +17,13 @@
package android.os;
import android.content.Context;
-import android.os.FileUtils;
-import android.os.FileUtils.FileStatus;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
import java.io.ByteArrayInputStream;
import java.io.File;
-import java.io.FileWriter;
-import java.io.FileNotFoundException;
import java.io.FileOutputStream;
-import java.io.IOException;
-
-import junit.framework.Assert;
+import java.io.FileWriter;
public class FileUtilsTest extends AndroidTestCase {
private static final String TEST_DATA =
@@ -60,60 +52,6 @@
if (mCopyFile.exists()) mCopyFile.delete();
}
- @LargeTest
- public void testGetFileStatus() {
- final byte[] MAGIC = { 0xB, 0xE, 0x0, 0x5 };
-
- try {
- // truncate test file and write MAGIC (4 bytes) to it.
- FileOutputStream os = new FileOutputStream(mTestFile, false);
- os.write(MAGIC, 0, 4);
- os.flush();
- os.close();
- } catch (FileNotFoundException e) {
- Assert.fail("File was removed durning test" + e);
- } catch (IOException e) {
- Assert.fail("Unexpected IOException: " + e);
- }
-
- Assert.assertTrue(mTestFile.exists());
- Assert.assertTrue(FileUtils.getFileStatus(mTestFile.getPath(), null));
-
- FileStatus status1 = new FileStatus();
- FileUtils.getFileStatus(mTestFile.getPath(), status1);
-
- Assert.assertEquals(4, status1.size);
-
- // Sleep for at least one second so that the modification time will be different.
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- }
-
- try {
- // append so we don't change the creation time.
- FileOutputStream os = new FileOutputStream(mTestFile, true);
- os.write(MAGIC, 0, 4);
- os.flush();
- os.close();
- } catch (FileNotFoundException e) {
- Assert.fail("File was removed durning test" + e);
- } catch (IOException e) {
- Assert.fail("Unexpected IOException: " + e);
- }
-
- FileStatus status2 = new FileStatus();
- FileUtils.getFileStatus(mTestFile.getPath(), status2);
-
- Assert.assertEquals(8, status2.size);
- Assert.assertTrue(status2.mtime > status1.mtime);
-
- mTestFile.delete();
-
- Assert.assertFalse(mTestFile.exists());
- Assert.assertFalse(FileUtils.getFileStatus(mTestFile.getPath(), null));
- }
-
// TODO: test setPermissions(), getPermissions()
@MediumTest
diff --git a/core/tests/coretests/src/android/os/ProcessTest.java b/core/tests/coretests/src/android/os/ProcessTest.java
index 598a8d2..1f5b7c8 100644
--- a/core/tests/coretests/src/android/os/ProcessTest.java
+++ b/core/tests/coretests/src/android/os/ProcessTest.java
@@ -18,7 +18,7 @@
package android.os;
import android.os.Process;
-import android.os.UserId;
+import android.os.UserHandle;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
@@ -32,10 +32,10 @@
assertEquals(android.os.Process.SYSTEM_UID, Process.getUidForName("system"));
assertEquals(Process.BLUETOOTH_UID, Process.getUidForName("bluetooth"));
assertEquals(Process.FIRST_APPLICATION_UID, Process.getUidForName("u0_a0"));
- assertEquals(UserId.getUid(1, Process.SYSTEM_UID), Process.getUidForName("u1_system"));
- assertEquals(UserId.getUid(2, Process.FIRST_ISOLATED_UID),
+ assertEquals(UserHandle.getUid(1, Process.SYSTEM_UID), Process.getUidForName("u1_system"));
+ assertEquals(UserHandle.getUid(2, Process.FIRST_ISOLATED_UID),
Process.getUidForName("u2_i0"));
- assertEquals(UserId.getUid(3, Process.FIRST_APPLICATION_UID + 100),
+ assertEquals(UserHandle.getUid(3, Process.FIRST_APPLICATION_UID + 100),
Process.getUidForName("u3_a100"));
}
diff --git a/data/etc/Android.mk b/data/etc/Android.mk
index 71a9a15..134ac0c 100644
--- a/data/etc/Android.mk
+++ b/data/etc/Android.mk
@@ -21,8 +21,6 @@
LOCAL_MODULE := platform.xml
-LOCAL_MODULE_TAGS := user
-
LOCAL_MODULE_CLASS := ETC
# This will install the file in /system/etc/permissions
@@ -38,8 +36,6 @@
#LOCAL_MODULE := required_hardware.xml
-#LOCAL_MODULE_TAGS := user
-
#LOCAL_MODULE_CLASS := ETC
# This will install the file in /system/etc/permissions
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index b352ffc..caeeb87 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -109,11 +109,11 @@
}
///////////////////////////////////////////////////////////////////////////////
-// CacheTextureLine
+// CacheTexture
///////////////////////////////////////////////////////////////////////////////
-bool CacheTextureLine::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
- if (glyph.fHeight + TEXTURE_BORDER_SIZE > mMaxHeight) {
+bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
+ if (glyph.fHeight + TEXTURE_BORDER_SIZE > mHeight) {
return false;
}
@@ -138,7 +138,7 @@
roundedUpW = glyphW;
}
*retOriginX = cacheBlock->mX;
- *retOriginY = mCurrentRow + cacheBlock->mY;
+ *retOriginY = cacheBlock->mY;
// If this is the remainder space, create a new cache block for this column. Otherwise,
// adjust the info about this column.
if (cacheBlock->mY == TEXTURE_BORDER_SIZE) {
@@ -146,10 +146,10 @@
// Adjust remainder space dimensions
cacheBlock->mWidth -= roundedUpW;
cacheBlock->mX += roundedUpW;
- if (mMaxHeight - glyphH >= glyphH) {
+ if (mHeight - glyphH >= glyphH) {
// There's enough height left over to create a new CacheBlock
CacheBlock *newBlock = new CacheBlock(oldX, glyphH, roundedUpW,
- mMaxHeight - glyphH);
+ mHeight - glyphH);
#if DEBUG_FONT_RENDERER
ALOGD("fitBitmap: Created new block: this, x, y, w, h = %p, %d, %d, %d, %d",
newBlock, newBlock->mX, newBlock->mY,
@@ -213,10 +213,10 @@
}
}
-void Font::invalidateTextureCache(CacheTextureLine *cacheLine) {
+void Font::invalidateTextureCache(CacheTexture *cacheTexture) {
for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
- if (cacheLine == NULL || cachedGlyph->mCachedTextureLine == cacheLine) {
+ if (cacheTexture == NULL || cachedGlyph->mCacheTexture == cacheTexture) {
cachedGlyph->mIsValid = false;
}
}
@@ -260,7 +260,7 @@
mState->appendMeshQuad(nPenX, nPenY, u1, v2,
nPenX + width, nPenY, u2, v2,
nPenX + width, nPenY - height, u2, v1,
- nPenX, nPenY - height, u1, v1, glyph->mCachedTextureLine->mCacheTexture);
+ nPenX, nPenY - height, u1, v1, glyph->mCacheTexture);
}
void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
@@ -271,7 +271,7 @@
uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
- CacheTexture *cacheTexture = glyph->mCachedTextureLine->mCacheTexture;
+ CacheTexture *cacheTexture = glyph->mCacheTexture;
uint32_t cacheWidth = cacheTexture->mWidth;
const uint8_t* cacheBuffer = cacheTexture->mTexture;
@@ -325,7 +325,7 @@
position->fY + destination[2].fY, u2, v1,
position->fX + destination[3].fX,
position->fY + destination[3].fY, u1, v1,
- glyph->mCachedTextureLine->mCacheTexture);
+ glyph->mCacheTexture);
}
CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
@@ -556,8 +556,8 @@
glyph->mBitmapWidth = skiaGlyph.fWidth;
glyph->mBitmapHeight = skiaGlyph.fHeight;
- uint32_t cacheWidth = glyph->mCachedTextureLine->mCacheTexture->mWidth;
- uint32_t cacheHeight = glyph->mCachedTextureLine->mCacheTexture->mHeight;
+ uint32_t cacheWidth = glyph->mCacheTexture->mWidth;
+ uint32_t cacheHeight = glyph->mCacheTexture->mHeight;
glyph->mBitmapMinU = startX / (float) cacheWidth;
glyph->mBitmapMinV = startY / (float) cacheHeight;
@@ -620,10 +620,6 @@
mTextMeshPtr = NULL;
mCurrentCacheTexture = NULL;
mLastCacheTexture = NULL;
- mCacheTextureSmall = NULL;
- mCacheTexture128 = NULL;
- mCacheTexture256 = NULL;
- mCacheTexture512 = NULL;
mLinearFiltering = false;
@@ -659,10 +655,10 @@
}
FontRenderer::~FontRenderer() {
- for (uint32_t i = 0; i < mCacheLines.size(); i++) {
- delete mCacheLines[i];
+ for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
+ delete mCacheTextures[i];
}
- mCacheLines.clear();
+ mCacheTextures.clear();
if (mInitialized) {
// Unbinding the buffer shouldn't be necessary but it crashes with some drivers
@@ -670,10 +666,6 @@
glDeleteBuffers(1, &mIndexBufferID);
delete[] mTextMeshPtr;
- delete mCacheTextureSmall;
- delete mCacheTexture128;
- delete mCacheTexture256;
- delete mCacheTexture512;
}
Vector<Font*> fontsToDereference = mActiveFonts;
@@ -692,29 +684,19 @@
mActiveFonts[i]->invalidateTextureCache();
}
- uint16_t totalGlyphs = 0;
- for (uint32_t i = 0; i < mCacheLines.size(); i++) {
- totalGlyphs += mCacheLines[i]->mNumGlyphs;
- mCacheLines[i]->init();
+ for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
+ mCacheTextures[i]->init();
}
-#if DEBUG_FONT_RENDERER
- // Erase caches, just as a debugging facility
- if (mCacheTextureSmall && mCacheTextureSmall->mTexture) {
- memset(mCacheTextureSmall->mTexture, 0,
- mCacheTextureSmall->mWidth * mCacheTextureSmall->mHeight);
- }
- if (mCacheTexture128 && mCacheTexture128->mTexture) {
- memset(mCacheTexture128->mTexture, 0,
- mCacheTexture128->mWidth * mCacheTexture128->mHeight);
- }
- if (mCacheTexture256 && mCacheTexture256->mTexture) {
- memset(mCacheTexture256->mTexture, 0,
- mCacheTexture256->mWidth * mCacheTexture256->mHeight);
- }
- if (mCacheTexture512 && mCacheTexture512->mTexture) {
- memset(mCacheTexture512->mTexture, 0,
- mCacheTexture512->mWidth * mCacheTexture512->mHeight);
+ #if DEBUG_FONT_RENDERER
+ uint16_t totalGlyphs = 0;
+ for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
+ totalGlyphs += mCacheTextures[i]->mNumGlyphs;
+ // Erase caches, just as a debugging facility
+ if (mCacheTextures[i]->mTexture) {
+ memset(mCacheTextures[i]->mTexture, 0,
+ mCacheTextures[i]->mWidth * mCacheTextures[i]->mHeight);
+ }
}
ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
#endif
@@ -730,38 +712,17 @@
}
void FontRenderer::flushLargeCaches() {
- if ((!mCacheTexture128 || !mCacheTexture128->mTexture) &&
- (!mCacheTexture256 || !mCacheTexture256->mTexture) &&
- (!mCacheTexture512 || !mCacheTexture512->mTexture)) {
- // Typical case; no large glyph caches allocated
- return;
- }
-
- for (uint32_t i = 0; i < mCacheLines.size(); i++) {
- CacheTextureLine* cacheLine = mCacheLines[i];
- if ((cacheLine->mCacheTexture == mCacheTexture128 ||
- cacheLine->mCacheTexture == mCacheTexture256 ||
- cacheLine->mCacheTexture == mCacheTexture512) &&
- cacheLine->mCacheTexture->mTexture != NULL) {
-#if DEBUG_FONT_RENDERER
- if (cacheLine->mCacheTexture == mCacheTexture128) {
- ALOGD("flushing cacheTexture128");
- } else if (cacheLine->mCacheTexture == mCacheTexture256) {
- ALOGD("flushing cacheTexture256");
- } else {
- ALOGD("flushing cacheTexture512");
+ // Start from 1; don't deallocate smallest/default texture
+ for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
+ CacheTexture* cacheTexture = mCacheTextures[i];
+ if (cacheTexture->mTexture != NULL) {
+ cacheTexture->init();
+ for (uint32_t j = 0; j < mActiveFonts.size(); j++) {
+ mActiveFonts[j]->invalidateTextureCache(cacheTexture);
}
-#endif
- cacheLine->init();
- for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
- mActiveFonts[i]->invalidateTextureCache(cacheLine);
- }
+ deallocateTextureMemory(cacheTexture);
}
}
-
- deallocateTextureMemory(mCacheTexture128);
- deallocateTextureMemory(mCacheTexture256);
- deallocateTextureMemory(mCacheTexture512);
}
void FontRenderer::allocateTextureMemory(CacheTexture* cacheTexture) {
@@ -789,12 +750,24 @@
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
+CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
+ uint32_t* startX, uint32_t* startY) {
+ for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
+ if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
+ return mCacheTextures[i];
+ }
+ }
+ // Could not fit glyph into current cache textures
+ return NULL;
+}
+
void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
uint32_t* retOriginX, uint32_t* retOriginY) {
checkInit();
cachedGlyph->mIsValid = false;
// If the glyph is too tall, don't cache it
- if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
+ if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
+ mCacheTextures[mCacheTextures.size() - 1]->mHeight) {
ALOGE("Font size too large to fit in cache. width, height = %i, %i",
(int) glyph.fWidth, (int) glyph.fHeight);
return;
@@ -804,36 +777,22 @@
uint32_t startX = 0;
uint32_t startY = 0;
- bool bitmapFit = false;
- CacheTextureLine *cacheLine;
- for (uint32_t i = 0; i < mCacheLines.size(); i++) {
- bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
- if (bitmapFit) {
- cacheLine = mCacheLines[i];
- break;
- }
- }
+ CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
// If the new glyph didn't fit, flush the state so far and invalidate everything
- if (!bitmapFit) {
+ if (!cacheTexture) {
flushAllAndInvalidate();
// Try to fit it again
- for (uint32_t i = 0; i < mCacheLines.size(); i++) {
- bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
- if (bitmapFit) {
- cacheLine = mCacheLines[i];
- break;
- }
- }
+ cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
// if we still don't fit, something is wrong and we shouldn't draw
- if (!bitmapFit) {
+ if (!cacheTexture) {
return;
}
}
- cachedGlyph->mCachedTextureLine = cacheLine;
+ cachedGlyph->mCacheTexture = cacheTexture;
*retOriginX = startX;
*retOriginY = startY;
@@ -841,9 +800,8 @@
uint32_t endX = startX + glyph.fWidth;
uint32_t endY = startY + glyph.fHeight;
- uint32_t cacheWidth = cacheLine->mMaxWidth;
+ uint32_t cacheWidth = cacheTexture->mWidth;
- CacheTexture* cacheTexture = cacheLine->mCacheTexture;
if (!cacheTexture->mTexture) {
// Large-glyph texture memory is allocated only as needed
allocateTextureMemory(cacheTexture);
@@ -896,17 +854,10 @@
}
void FontRenderer::initTextTexture() {
- for (uint32_t i = 0; i < mCacheLines.size(); i++) {
- delete mCacheLines[i];
+ for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
+ delete mCacheTextures[i];
}
- mCacheLines.clear();
-
- if (mCacheTextureSmall) {
- delete mCacheTextureSmall;
- delete mCacheTexture128;
- delete mCacheTexture256;
- delete mCacheTexture512;
- }
+ mCacheTextures.clear();
// Next, use other, separate caches for large glyphs.
uint16_t maxWidth = 0;
@@ -918,35 +869,12 @@
maxWidth = MAX_TEXT_CACHE_WIDTH;
}
- mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true);
- mCacheTexture128 = createCacheTexture(maxWidth, 256, false);
- mCacheTexture256 = createCacheTexture(maxWidth, 256, false);
- mCacheTexture512 = createCacheTexture(maxWidth, 512, false);
- mCurrentCacheTexture = mCacheTextureSmall;
-
mUploadTexture = false;
- // Split up our default cache texture into lines of certain widths
- int nextLine = 0;
- mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, mCacheTextureSmall));
- nextLine += mCacheLines.top()->mMaxHeight;
- mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, mCacheTextureSmall));
- nextLine += mCacheLines.top()->mMaxHeight;
- mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, mCacheTextureSmall));
- nextLine += mCacheLines.top()->mMaxHeight;
- mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, mCacheTextureSmall));
- nextLine += mCacheLines.top()->mMaxHeight;
- mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, mCacheTextureSmall));
- nextLine += mCacheLines.top()->mMaxHeight;
- mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, mCacheTextureSmall));
- nextLine += mCacheLines.top()->mMaxHeight;
- mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine,
- nextLine, mCacheTextureSmall));
-
- // The first cache is split into 2 lines of height 128, the rest have just one cache line.
- mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, mCacheTexture128));
- mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, mCacheTexture128));
- mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, mCacheTexture256));
- mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, mCacheTexture512));
+ mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
+ mCacheTextures.push(createCacheTexture(maxWidth, 256, false));
+ mCacheTextures.push(createCacheTexture(maxWidth, 256, false));
+ mCacheTextures.push(createCacheTexture(maxWidth, 512, false));
+ mCurrentCacheTexture = mCacheTextures[0];
}
// Avoid having to reallocate memory and render quad by quad
@@ -1001,16 +929,14 @@
Caches& caches = Caches::getInstance();
GLuint lastTextureId = 0;
- // Iterate over all the cache lines and see which ones need to be updated
- for (uint32_t i = 0; i < mCacheLines.size(); i++) {
- CacheTextureLine* cl = mCacheLines[i];
- if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) {
- CacheTexture* cacheTexture = cl->mCacheTexture;
+ // Iterate over all the cache textures and see which ones need to be updated
+ for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
+ CacheTexture* cacheTexture = mCacheTextures[i];
+ if (cacheTexture->mDirty && cacheTexture->mTexture != NULL) {
uint32_t xOffset = 0;
- uint32_t yOffset = cl->mCurrentRow;
- uint32_t width = cl->mMaxWidth;
- uint32_t height = cl->mMaxHeight;
- void* textureData = cacheTexture->mTexture + (yOffset * width);
+ uint32_t width = cacheTexture->mWidth;
+ uint32_t height = cacheTexture->mHeight;
+ void* textureData = cacheTexture->mTexture;
if (cacheTexture->mTextureId != lastTextureId) {
caches.activeTexture(0);
@@ -1018,13 +944,13 @@
lastTextureId = cacheTexture->mTextureId;
}
#if DEBUG_FONT_RENDERER
- ALOGD("glTextSubimage for cacheLine %d: xOff, yOff, width height = %d, %d, %d, %d", i,
- xOffset, yOffset, width, height);
+ ALOGD("glTextSubimage for cacheTexture %d: xOff, width height = %d, %d, %d",
+ i, xOffset, width, height);
#endif
- glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
+ glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, 0, width, height,
GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
- cl->mDirty = false;
+ cacheTexture->mDirty = false;
}
}
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 8b1d10c8..febae17 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -61,35 +61,14 @@
class FontRenderer;
-class CacheTexture {
-public:
- CacheTexture(uint16_t width, uint16_t height) :
- mTexture(NULL), mTextureId(0), mWidth(width), mHeight(height),
- mLinearFiltering(false) { }
- ~CacheTexture() {
- if (mTexture) {
- delete[] mTexture;
- }
- if (mTextureId) {
- glDeleteTextures(1, &mTextureId);
- }
- }
-
- uint8_t* mTexture;
- GLuint mTextureId;
- uint16_t mWidth;
- uint16_t mHeight;
- bool mLinearFiltering;
-};
-
/**
- * CacheBlock is a noce in a linked list of current free space areas in a CacheTextureLine.
- * Using CacheBlocks enables us to pack the cache line from top to bottom as well as left to right.
+ * CacheBlock is a node in a linked list of current free space areas in a CacheTexture.
+ * Using CacheBlocks enables us to pack the cache from top to bottom as well as left to right.
* When we add a glyph to the cache, we see if it fits within one of the existing columns that
* have already been started (this is the case if the glyph fits vertically as well as
* horizontally, and if its width is sufficiently close to the column width to avoid
* sub-optimal packing of small glyphs into wide columns). If there is no column in which the
- * glyph fits, we check the final node, which is the remaining space in the cache line, creating
+ * glyph fits, we check the final node, which is the remaining space in the cache, creating
* a new column as appropriate.
*
* As columns fill up, we remove their CacheBlock from the list to avoid having to check
@@ -122,21 +101,22 @@
}
};
-class CacheTextureLine {
+class CacheTexture {
public:
- CacheTextureLine(uint16_t maxWidth, uint16_t maxHeight, uint32_t currentRow,
- CacheTexture* cacheTexture):
- mMaxHeight(maxHeight),
- mMaxWidth(maxWidth),
- mCurrentRow(currentRow),
- mDirty(false),
- mNumGlyphs(0),
- mCacheTexture(cacheTexture) {
+ CacheTexture(uint16_t width, uint16_t height) :
+ mTexture(NULL), mTextureId(0), mWidth(width), mHeight(height),
+ mLinearFiltering(false), mDirty(false), mNumGlyphs(0) {
mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
- maxWidth - TEXTURE_BORDER_SIZE, maxHeight - TEXTURE_BORDER_SIZE, true);
+ mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
}
- ~CacheTextureLine() {
+ ~CacheTexture() {
+ if (mTexture) {
+ delete[] mTexture;
+ }
+ if (mTextureId) {
+ glDeleteTextures(1, &mTextureId);
+ }
reset();
}
@@ -154,17 +134,18 @@
// reset, then create a new remainder space to start again
reset();
mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
- mMaxWidth - TEXTURE_BORDER_SIZE, mMaxHeight - TEXTURE_BORDER_SIZE, true);
+ mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
}
bool fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY);
- uint16_t mMaxHeight;
- uint16_t mMaxWidth;
- uint32_t mCurrentRow;
+ uint8_t* mTexture;
+ GLuint mTextureId;
+ uint16_t mWidth;
+ uint16_t mHeight;
+ bool mLinearFiltering;
bool mDirty;
uint16_t mNumGlyphs;
- CacheTexture* mCacheTexture;
CacheBlock* mCacheBlocks;
};
@@ -193,7 +174,7 @@
// Auto-kerning
SkFixed mLsbDelta;
SkFixed mRsbDelta;
- CacheTextureLine* mCachedTextureLine;
+ CacheTexture* mCacheTexture;
};
@@ -260,7 +241,7 @@
// Cache of glyphs
DefaultKeyedVector<glyph_t, CachedGlyphInfo*> mCachedGlyphs;
- void invalidateTextureCache(CacheTextureLine *cacheLine = NULL);
+ void invalidateTextureCache(CacheTexture *cacheTexture = NULL);
CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph);
void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph);
@@ -364,17 +345,11 @@
uint32_t getCacheSize() const {
uint32_t size = 0;
- if (mCacheTextureSmall != NULL && mCacheTextureSmall->mTexture != NULL) {
- size += mCacheTextureSmall->mWidth * mCacheTextureSmall->mHeight;
- }
- if (mCacheTexture128 != NULL && mCacheTexture128->mTexture != NULL) {
- size += mCacheTexture128->mWidth * mCacheTexture128->mHeight;
- }
- if (mCacheTexture256 != NULL && mCacheTexture256->mTexture != NULL) {
- size += mCacheTexture256->mWidth * mCacheTexture256->mHeight;
- }
- if (mCacheTexture512 != NULL && mCacheTexture512->mTexture != NULL) {
- size += mCacheTexture512->mWidth * mCacheTexture512->mHeight;
+ for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
+ CacheTexture* cacheTexture = mCacheTextures[i];
+ if (cacheTexture != NULL && cacheTexture->mTexture != NULL) {
+ size += cacheTexture->mWidth * cacheTexture->mHeight;
+ }
}
return size;
}
@@ -390,6 +365,7 @@
CacheTexture* createCacheTexture(int width, int height, bool allocate);
void cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
uint32_t *retOriginX, uint32_t *retOriginY);
+ CacheTexture* cacheBitmapInTexture(const SkGlyph& glyph, uint32_t* startX, uint32_t* startY);
void flushAllAndInvalidate();
void initVertexArrayBuffers();
@@ -415,17 +391,13 @@
uint32_t mSmallCacheWidth;
uint32_t mSmallCacheHeight;
- Vector<CacheTextureLine*> mCacheLines;
+ Vector<CacheTexture*> mCacheTextures;
Font* mCurrentFont;
Vector<Font*> mActiveFonts;
CacheTexture* mCurrentCacheTexture;
CacheTexture* mLastCacheTexture;
- CacheTexture* mCacheTextureSmall;
- CacheTexture* mCacheTexture128;
- CacheTexture* mCacheTexture256;
- CacheTexture* mCacheTexture512;
void checkTextureUpdate();
bool mUploadTexture;
diff --git a/location/java/android/location/Criteria.java b/location/java/android/location/Criteria.java
index 6258a43..68fb4a0 100644
--- a/location/java/android/location/Criteria.java
+++ b/location/java/android/location/Criteria.java
@@ -24,7 +24,9 @@
* location provider. Providers maybe ordered according to accuracy,
* power usage, ability to report altitude, speed,
* and bearing, and monetary cost.
- * @deprecated {@link LocationRequest} instead
+ *
+ * @deprecated use {@link LocationRequest} instead, and also see notes
+ * at {@link LocationManager}
*/
@Deprecated
public class Criteria implements Parcelable {
diff --git a/location/java/android/location/Geofence.java b/location/java/android/location/Geofence.java
index 353a1ca..03cca84 100644
--- a/location/java/android/location/Geofence.java
+++ b/location/java/android/location/Geofence.java
@@ -20,7 +20,11 @@
import android.os.Parcelable;
/**
- * Represents a Geofence
+ * Represents a geographical boundary, also known as a geofence.
+ *
+ * <p>Currently only circular geofences are supported, but this object
+ * is opaque so could be used in the future to represent polygons or other
+ * shapes.
*/
public final class Geofence implements Parcelable {
/** @hide */
@@ -33,6 +37,7 @@
/**
* Create a horizontal, circular geofence.
+ *
* @param latitude latitude in degrees
* @param longitude longitude in degrees
* @param radius radius in meters
@@ -152,6 +157,9 @@
return result;
}
+ /**
+ * Two geofences are equal if they have identical properties.
+ */
@Override
public boolean equals(Object obj) {
if (this == obj)
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index ece4500..f50ee82 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -27,15 +27,15 @@
import java.util.StringTokenizer;
/**
- * A class representing a geographic location sensed at a particular
- * time (a "fix"). A location consists of a latitude and longitude, a
- * UTC timestamp. and optionally information on altitude, speed, and
- * bearing.
+ * A data class representing a geographic location.
*
- * <p> Information specific to a particular provider or class of
- * providers may be communicated to the application using getExtras,
- * which returns a Bundle of key/value pairs. Each provider will only
- * provide those entries for which information is available.
+ * <p>A location can consist of a latitude, longitude, timestamp,
+ * and other information such as bearing, altitude and velocity.
+ *
+ * <p>All locations generated by the {@link LocationManager} are
+ * guaranteed to have a valid latitude, longitude, and timestamp
+ * (both UTC time and elapsed real-time since boot), all other
+ * parameters are optional.
*/
public class Location implements Parcelable {
/**
@@ -86,20 +86,19 @@
private float[] mResults = new float[2];
/**
- * Constructs a new Location. By default, time, latitude,
- * longitude, and numSatellites are 0; hasAltitude, hasSpeed, and
- * hasBearing are false; and there is no extra information.
+ * Construct a new Location with a named provider.
*
- * @param provider the name of the location provider that generated this
- * location fix.
+ * <p>By default time, latitude and longitude are 0, and the location
+ * has no bearing, altitude, speed, accuracy or extras.
+ *
+ * @param provider the name of the provider that generated this location
*/
public Location(String provider) {
mProvider = provider;
}
/**
- * Constructs a new Location object that is a copy of the given
- * location.
+ * Construct a new Location object that is copied from an existing one.
*/
public Location(Location l) {
set(l);
@@ -447,9 +446,19 @@
}
/**
- * Returns the name of the provider that generated this fix,
- * or null if it is not associated with a provider.
+ * Returns the name of the provider that generated this fix.
+ *
+ * <p class="note">At API version 17 we deprecated {@link LocationProvider}
+ * and all API methods that request a provider by name. The new API methods
+ * will produce locations that could come from different sources, and even
+ * locations that are fused from several sources. So you should generally
+ * not care what provider is associated with a location object.
+ *
+ * @return the provider, or null if it has not been set
+ *
+ * @deprecated locations can now be sourced from many providers, or even fused
*/
+ @Deprecated
public String getProvider() {
return mProvider;
}
@@ -462,16 +471,19 @@
}
/**
- * Return the UTC time of this fix, in milliseconds since January 1,
- * 1970.
+ * Return the UTC time of this fix, in milliseconds since January 1, 1970.
+ *
* <p>Note that the UTC time on a device is not monotonic: it
* can jump forwards or backwards unpredictably. So always use
- * {@link #getElapsedRealtimeNano()} when calculating time deltas.
- * <p>On the other hand, {@link #getTime()} is useful for presenting
+ * {@link #getElapsedRealtimeNano} when calculating time deltas.
+ *
+ * <p>On the other hand, {@link #getTime} is useful for presenting
* a human readable time to the user, or for carefully comparing
* location fixes across reboot or across devices.
- * <p>This method will always return a valid timestamp on
- * Locations generated by a {@link LocationProvider}.
+ *
+ * <p>All locations generated by the {@link LocationManager}
+ * are guaranteed to have a valid UTC time, however remember that
+ * the system time may have changed since the location was generated.
*
* @return time of fix, in milliseconds since January 1, 1970.
*/
@@ -491,13 +503,16 @@
/**
* Return the time of this fix, in elapsed real-time since system boot.
+ *
* <p>This value can be reliably compared to
- * {@link android.os.SystemClock#elapsedRealtimeNano()},
- * to calculate the age of a fix, and to compare Location fixes, since
- * elapsed real-time is guaranteed monotonic for each system boot, and
- * continues to increment even when the system is in deep sleep.
- * <p>This method will always return a valid timestamp on
- * Locations generated by a {@link LocationProvider}.
+ * {@link android.os.SystemClock#elapsedRealtimeNano},
+ * to calculate the age of a fix and to compare Location fixes. This
+ * is reliable because elapsed real-time is guaranteed monotonic for
+ * each system boot and continues to increment even when the system
+ * is in deep sleep (unlike {@link #getTime}.
+ *
+ * <p>All locations generated by the {@link LocationManager}
+ * are guaranteed to have a valid elapsed real-time.
*
* @return elapsed real-time of fix, in nanoseconds since system boot.
*/
@@ -515,56 +530,59 @@
}
/**
- * Return the latitude of this fix.
- * <p>This method will always return a valid latitude on
- * Locations generated by a {@link LocationProvider}.
+ * Get the latitude, in degrees.
+ *
+ * <p>All locations generated by the {@link LocationManager}
+ * will have a valid latitude.
*/
public double getLatitude() {
return mLatitude;
}
/**
- * Sets the latitude of this fix.
+ * Set the latitude, in degrees.
*/
public void setLatitude(double latitude) {
mLatitude = latitude;
}
/**
- * Return the longitude of this fix.
- * <p>This method will always return a valid longitude on
- * Locations generated by a {@link LocationProvider}.
+ * Get the longitude, in degrees.
+ *
+ * <p>All locations generated by the {@link LocationManager}
+ * will have a valid longitude.
*/
public double getLongitude() {
return mLongitude;
}
/**
- * Sets the longitude of this fix.
+ * Set the longitude, in degrees.
*/
public void setLongitude(double longitude) {
mLongitude = longitude;
}
/**
- * Returns true if this fix contains altitude information, false
- * otherwise.
+ * True if this location has an altitude.
*/
public boolean hasAltitude() {
return mHasAltitude;
}
/**
- * Returns the altitude of this fix. If {@link #hasAltitude} is false,
- * 0.0f is returned.
+ * Get the altitude if available, in meters above sea level.
+ *
+ * <p>If this location does not have an altitude then 0.0 is returned.
*/
public double getAltitude() {
return mAltitude;
}
/**
- * Sets the altitude of this fix. Following this call,
- * hasAltitude() will return true.
+ * Set the altitude, in meters above sea level.
+ *
+ * <p>Following this call {@link #hasAltitude} will return true.
*/
public void setAltitude(double altitude) {
mAltitude = altitude;
@@ -572,8 +590,10 @@
}
/**
- * Clears the altitude of this fix. Following this call,
- * hasAltitude() will return false.
+ * Remove the altitude from this location.
+ *
+ * <p>Following this call {@link #hasAltitude} will return false,
+ * and {@link #getAltitude} will return 0.0.
*/
public void removeAltitude() {
mAltitude = 0.0f;
@@ -581,24 +601,25 @@
}
/**
- * Returns true if this fix contains speed information, false
- * otherwise. The default implementation returns false.
+ * True if this location has a speed.
*/
public boolean hasSpeed() {
return mHasSpeed;
}
/**
- * Returns the speed of the device over ground in meters/second.
- * If hasSpeed() is false, 0.0f is returned.
+ * Get the speed if it is available, in meters/second over ground.
+ *
+ * <p>If this location does not have a speed then 0.0 is returned.
*/
public float getSpeed() {
return mSpeed;
}
/**
- * Sets the speed of this fix, in meters/second. Following this
- * call, hasSpeed() will return true.
+ * Set the speed, in meters/second over ground.
+ *
+ * <p>Following this call {@link #hasSpeed} will return true.
*/
public void setSpeed(float speed) {
mSpeed = speed;
@@ -606,8 +627,10 @@
}
/**
- * Clears the speed of this fix. Following this call, hasSpeed()
- * will return false.
+ * Remove the speed from this location.
+ *
+ * <p>Following this call {@link #hasSpeed} will return false,
+ * and {@link #getSpeed} will return 0.0.
*/
public void removeSpeed() {
mSpeed = 0.0f;
@@ -615,24 +638,32 @@
}
/**
- * Returns true if the provider is able to report bearing information,
- * false otherwise. The default implementation returns false.
+ * True if this location has a bearing.
*/
public boolean hasBearing() {
return mHasBearing;
}
/**
- * Returns the direction of travel in degrees East of true
- * North. If hasBearing() is false, 0.0 is returned.
+ * Get the bearing, in degrees.
+ *
+ * <p>Bearing is the horizontal direction of travel of this device,
+ * and is not related to the device orientation. It is guaranteed to
+ * be in the range (0.0, 360.0] if the device has a bearing.
+ *
+ * <p>If this location does not have a bearing then 0.0 is returned.
*/
public float getBearing() {
return mBearing;
}
/**
- * Sets the bearing of this fix. Following this call, hasBearing()
- * will return true.
+ * Set the bearing, in degrees.
+ *
+ * <p>Bearing is the horizontal direction of travel of this device,
+ * and is not related to the device orientation.
+ *
+ * <p>The input will be wrapped into the range (0.0, 360.0].
*/
public void setBearing(float bearing) {
while (bearing < 0.0f) {
@@ -646,8 +677,10 @@
}
/**
- * Clears the bearing of this fix. Following this call, hasBearing()
- * will return false.
+ * Remove the bearing from this location.
+ *
+ * <p>Following this call {@link #hasBearing} will return false,
+ * and {@link #getBearing} will return 0.0.
*/
public void removeBearing() {
mBearing = 0.0f;
@@ -655,35 +688,47 @@
}
/**
- * Return true if this Location has an associated accuracy.
- * <p>All Location objects generated by a {@link LocationProvider}
- * will have an accuracy.
+ * True if this location has an accuracy.
+ *
+ * <p>All locations generated by the {@link LocationManager} have an
+ * accuracy.
*/
public boolean hasAccuracy() {
return mHasAccuracy;
}
/**
- * Return the accuracy of this Location fix.
- * <p>Accuracy is measured in meters, and indicates the
- * radius of 95% confidence.
- * In other words, there is a 95% probability that the
- * true location is within a circle centered at the reported
- * location, with radius of the reported accuracy.
- * <p>This is only a measure of horizontal accuracy, and does
- * not indicate the accuracy of bearing, velocity or altitude
- * if those are included in this Location.
- * <p>If {@link #hasAccuracy} is false, 0.0 is returned.
- * <p>All Location object generated by a {@link LocationProvider}
- * will have a valid accuracy.
+ * Get the estimated accuracy of this location, in meters.
+ *
+ * <p>We define accuracy as the radius of 68% confidence. In other
+ * words, if you draw a circle centered at this location's
+ * latitude and longitude, and with a radius equal to the accuracy,
+ * then there is a 68% probability that the true location is inside
+ * the circle.
+ *
+ * <p>In statistical terms, it is assumed that location errors
+ * are random with a normal distribution, so the 68% confidence circle
+ * represents one standard deviation. Note that in practice, location
+ * errors do not always follow such a simple distribution.
+ *
+ * <p>This accuracy estimation is only concerned with horizontal
+ * accuracy, and does not indicate the accuracy of bearing,
+ * velocity or altitude if those are included in this Location.
+ *
+ * <p>If this location does not have an accuracy, then 0.0 is returned.
+ * All locations generated by the {@link LocationManager} include
+ * an accuracy.
*/
public float getAccuracy() {
return mAccuracy;
}
/**
- * Sets the accuracy of this fix. Following this call, hasAccuracy()
- * will return true.
+ * Set the estimated accuracy of this location, meters.
+ *
+ * <p>See {@link #getAccuracy} for the definition of accuracy.
+ *
+ * <p>Following this call {@link #hasAccuracy} will return true.
*/
public void setAccuracy(float accuracy) {
mAccuracy = accuracy;
@@ -691,8 +736,10 @@
}
/**
- * Clears the accuracy of this fix. Following this call, hasAccuracy()
- * will return false.
+ * Remove the accuracy from this location.
+ *
+ * <p>Following this call {@link #hasAccuracy} will return false, and
+ * {@link #getAccuracy} will return 0.0.
*/
public void removeAccuracy() {
mAccuracy = 0.0f;
@@ -700,8 +747,14 @@
}
/**
- * Return true if this Location object has enough data set to
- * be considered a valid fix from a {@link LocationProvider}.
+ * Return true if this Location object is complete.
+ *
+ * <p>A location object is currently considered complete if it has
+ * a valid provider, accuracy, wall-clock time and elapsed real-time.
+ *
+ * <p>All locations supplied by the {@link LocationManager} to
+ * applications must be complete.
+ *
* @see #makeComplete
* @hide
*/
@@ -714,9 +767,11 @@
}
/**
- * Helper to fill in incomplete fields.
- * Only use this to assist in backwards compatibility
- * with Location objects received from applications.
+ * Helper to fill incomplete fields.
+ *
+ * <p>Used to assist in backwards compatibility with
+ * Location objects received from applications.
+ *
* @see #isComplete
* @hide
*/
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 5f87c84..25da208 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -46,12 +46,41 @@
* {@link android.content.Context#getSystemService
* Context.getSystemService(Context.LOCATION_SERVICE)}.
*
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For more information about using location services, read the
- * <a href="{@docRoot}guide/topics/location/index.html">Location and Maps</a>
- * developer guide.</p>
- * </div>
+ * <p>At API version 17 the Location API's were simplified.
+ * Previously applications would need to explicitly enumerate, select, and
+ * track Location Providers (such as GPS or Network).
+ * This has been replaced by the concept of
+ * <em>Fused Location</em>. Now applications just specify the quality of service
+ * required for location updates (using the new {@link LocationRequest} class),
+ * and the system will fuse results from individual location providers
+ * as necessary before returning the result to the application.
+ *
+ * <p>As a result of this change, the {@link LocationProvider} and
+ * {@link Criteria} classes have been deprecated, in favor of
+ * {@link LocationRequest}. Furthermore, all Location Manager
+ * methods involving Criteria or explicitly named Providers have
+ * been deprecated, in favor of new variants that use
+ * {@link LocationRequest}.
+ *
+ * <p>A single {@link LocationRequest} object can trigger the use
+ * of all providers (including GPS, Network, and the passive) provider
+ * as necessary. This should result in a lot less work for your application. You
+ * no longer need to track the status and availability of each
+ * location provider. Just set the quality of locations required
+ * in {@link LocationRequest}, and let the system manage the rest.
+ *
+ * <p class="note">Unless noted, all Location API methods require
+ * the {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} or
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permissions.
+ * If your application only has the Coarse permission then it will still
+ * receive location results, but the update rate will be throttled and
+ * the exact location will be obfuscated to a coarse level of accuracy.
+ *
+ * <p> class="note">Before API level 17, the use of 'fine' location
+ * providers such as GPS required the fine permission. As of API level
+ * 17, applications with only the coarse permission may use all providers,
+ * including GPS, but the locations are obfuscated (made coarse) before
+ * being sent to the application.
*/
public class LocationManager {
private static final String TAG = "LocationManager";
@@ -65,56 +94,69 @@
private final GpsStatus mGpsStatus = new GpsStatus();
/**
- * Name of the network location provider. This provider determines location based on
+ * Name of the network location provider.
+ * <p>This provider determines location based on
* availability of cell tower and WiFi access points. Results are retrieved
* by means of a network lookup.
*
- * Requires either of the permissions android.permission.ACCESS_COARSE_LOCATION
- * or android.permission.ACCESS_FINE_LOCATION.
- * @deprecated use the {@link Criteria} class instead
+ * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
*/
@Deprecated
public static final String NETWORK_PROVIDER = "network";
/**
- * Name of the GPS location provider. This provider determines location using
+ * Name of the GPS location provider.
+ *
+ * <p>This provider determines location using
* satellites. Depending on conditions, this provider may take a while to return
* a location fix.
*
- * Requires the permission android.permission.ACCESS_FINE_LOCATION.
+ * <p>Before API version 17, this provider required the
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission.
+ * From API version 17 and onwards, this provider can also be used with
+ * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}, however
+ * the locations returned will be obfuscated to a coarse level of accuracy.
*
* <p> The extras Bundle for the GPS location provider can contain the
* following key/value pairs:
- *
* <ul>
* <li> satellites - the number of satellites used to derive the fix
* </ul>
- * @deprecated use the {@link Criteria} class instead
+ *
+ * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
*/
@Deprecated
public static final String GPS_PROVIDER = "gps";
/**
* A special location provider for receiving locations without actually initiating
- * a location fix. This provider can be used to passively receive location updates
+ * a location fix.
+ *
+ * <p>This provider can be used to passively receive location updates
* when other applications or services request them without actually requesting
* the locations yourself. This provider will return locations generated by other
* providers. You can query the {@link Location#getProvider()} method to determine
* the origin of the location update.
*
- * Requires the permission android.permission.ACCESS_FINE_LOCATION, although if the GPS
- * is not enabled this provider might only return coarse fixes.
- * @deprecated use the {@link Criteria} class instead
+ * <p>Before API version 17, this provider required the
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission.
+ * From API version 17 and onwards, this provider can also be used with
+ * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}, however
+ * the locations returned will be obfuscated to a coarse level of accuracy.
+ *
+ * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
*/
@Deprecated
public static final String PASSIVE_PROVIDER = "passive";
/**
- * Name of the Fused location provider.<p>
- * This provider combines inputs for all possible location sources
- * to provide the best possible Location fix.<p>
+ * Name of the Fused location provider.
*
- * Requires the permission android.permission.ACCESS_FINE_LOCATION.
+ * <p>This provider combines inputs for all possible location sources
+ * to provide the best possible Location fix. It is implicitly
+ * used for all API's that involve the {@link LocationRequest}
+ * object.
+ *
* @hide
*/
public static final String FUSED_PROVIDER = "fused";
@@ -128,7 +170,8 @@
/**
* Key used for a Bundle extra holding an Integer status value
* when a status change is broadcast using a PendingIntent.
- * @deprecated use the {@link Criteria} class instead
+ *
+ * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
*/
@Deprecated
public static final String KEY_STATUS_CHANGED = "status";
@@ -136,7 +179,8 @@
/**
* Key used for a Bundle extra holding an Boolean status value
* when a provider enabled/disabled event is broadcast using a PendingIntent.
- * @deprecated use the {@link Criteria} class instead
+ *
+ * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
*/
@Deprecated
public static final String KEY_PROVIDER_ENABLED = "providerEnabled";
@@ -153,7 +197,7 @@
* where {@code true} means enabled.
* @see #EXTRA_GPS_ENABLED
*
- * {@hide}
+ * @hide
*/
public static final String GPS_ENABLED_CHANGE_ACTION =
"android.location.GPS_ENABLED_CHANGE";
@@ -161,7 +205,8 @@
/**
* Broadcast intent action when the configured location providers
* change.
- * @deprecated use the {@link Criteria} class instead
+ *
+ * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
*/
@Deprecated
public static final String PROVIDERS_CHANGED_ACTION =
@@ -173,7 +218,7 @@
* boolean, where {@code true} means that the GPS is actively receiving fixes.
* @see #EXTRA_GPS_ENABLED
*
- * {@hide}
+ * @hide
*/
public static final String GPS_FIX_CHANGE_ACTION =
"android.location.GPS_FIX_CHANGE";
@@ -183,7 +228,7 @@
* disabled. {@code true} means GPS is enabled. Retrieve it with
* {@link android.content.Intent#getBooleanExtra(String,boolean)}.
*
- * {@hide}
+ * @hide
*/
public static final String EXTRA_GPS_ENABLED = "enabled";
@@ -285,6 +330,7 @@
}
}
}
+
/**
* @hide - hide this constructor because it has a parameter
* of type ILocationManager, which is a system private class. The
@@ -301,12 +347,13 @@
}
/**
- * Returns a list of the names of all known location providers. All
- * providers are returned, including ones that are not permitted to be
- * accessed by the calling activity or are currently disabled.
+ * Returns a list of the names of all known location providers.
+ * <p>All providers are returned, including ones that are not permitted to
+ * be accessed by the calling activity or are currently disabled.
*
- * @return list of Strings containing names of the providers
- * @deprecated use the {@link Criteria} class instead
+ * @return list of Strings containing names of the provider
+ *
+ * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
*/
@Deprecated
public List<String> getAllProviders() {
@@ -319,15 +366,13 @@
}
/**
- * Returns a list of the names of location providers. Only providers that
- * are permitted to be accessed by the calling activity will be returned.
+ * Returns a list of the names of location providers.
*
* @param enabledOnly if true then only the providers which are currently
* enabled are returned.
* @return list of Strings containing names of the providers
- * @deprecated The {@link LocationProvider} class is deprecated. So
- * use the {@link Criteria} class to request location instead of
- * enumerating providers.
+ *
+ * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
*/
@Deprecated
public List<String> getProviders(boolean enabledOnly) {
@@ -346,12 +391,11 @@
* @param name the provider name
* @return a LocationProvider, or null
*
- * @throws IllegalArgumentException if name is null or does not exisit
+ * @throws IllegalArgumentException if name is null or does not exist
* @throws SecurityException if the caller is not permitted to access the
* given provider.
- * @deprecated The {@link LocationProvider} class is deprecated. So
- * use the {@link Criteria} class to request location instead of
- * enumerating providers.
+ *
+ * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
*/
@Deprecated
public LocationProvider getProvider(String name) {
@@ -377,9 +421,8 @@
* @param enabledOnly if true then only the providers which are currently
* enabled are returned.
* @return list of Strings containing names of the providers
- * @deprecated The {@link LocationProvider} class is deprecated. So
- * use the {@link Criteria} class to request location instead of
- * enumerating providers.
+ *
+ * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
*/
@Deprecated
public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
@@ -413,7 +456,8 @@
* @param criteria the criteria that need to be matched
* @param enabledOnly if true then only a provider that is currently enabled is returned
* @return name of the provider that best matches the requirements
- * @deprecated using an explicit provider doesn't allow fused location
+ *
+ * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
*/
@Deprecated
public String getBestProvider(Criteria criteria, boolean enabledOnly) {
@@ -427,64 +471,11 @@
}
/**
- * Registers the current activity to be notified periodically by
- * the named provider. Periodically, the supplied LocationListener will
- * be called with the current Location or with status updates.
+ * Register for location updates using the named provider, and a
+ * pending intent.
*
- * <p> It may take a while to receive the first location update. If
- * an immediate location is required, applications may use the
- * {@link #getLastKnownLocation(String)} method.
- *
- * <p> In case the provider is disabled by the user, updates will stop,
- * and the {@link LocationListener#onProviderDisabled(String)}
- * method will be called. As soon as the provider is enabled again,
- * the {@link LocationListener#onProviderEnabled(String)} method will
- * be called and location updates will start again.
- *
- * <p> The update interval can be controlled using the minTime parameter.
- * The elapsed time between location updates will never be less than
- * minTime, although it can be more depending on the Location Provider
- * implementation and the update interval requested by other applications.
- *
- * <p> Choosing a sensible value for minTime is important to conserve
- * battery life. Each location update requires power from
- * GPS, WIFI, Cell and other radios. Select a minTime value as high as
- * possible while still providing a reasonable user experience.
- * If your application is not in the foreground and showing
- * location to the user then your application should avoid using an active
- * provider (such as {@link #NETWORK_PROVIDER} or {@link #GPS_PROVIDER}),
- * but if you insist then select a minTime of 5 * 60 * 1000 (5 minutes)
- * or greater. If your application is in the foreground and showing
- * location to the user then it is appropriate to select a faster
- * update interval.
- *
- * <p> The minDistance parameter can also be used to control the
- * frequency of location updates. If it is greater than 0 then the
- * location provider will only send your application an update when
- * the location has changed by at least minDistance meters, AND
- * at least minTime milliseconds have passed. However it is more
- * difficult for location providers to save power using the minDistance
- * parameter, so minTime should be the primary tool to conserving battery
- * life.
- *
- * <p> If your application wants to passively observe location
- * updates triggered by other applications, but not consume
- * any additional power otherwise, then use the {@link #PASSIVE_PROVIDER}
- * This provider does not actively turn on or modify active location
- * providers, so you do not need to be as careful about minTime and
- * minDistance. However if your application performs heavy work
- * on a location update (such as network activity) then you should
- * select non-zero values for minTime and/or minDistance to rate-limit
- * your update frequency in the case another application enables a
- * location provider with extremely fast updates.
- *
- * <p> The calling thread must be a {@link android.os.Looper} thread such as
- * the main thread of the calling Activity.
- *
- * <p class="note"> Prior to Jellybean, the minTime parameter was
- * only a hint, and some location provider implementations ignored it.
- * From Jellybean and onwards it is mandatory for Android compatible
- * devices to observe both the minTime and minDistance parameters.
+ * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
+ * for more detail on how to use this (deprecated) method.
*
* @param provider the name of the provider with which to register
* @param minTime minimum time interval between location updates, in milliseconds
@@ -497,8 +488,8 @@
* on this device
* @throws IllegalArgumentException if listener is null
* @throws RuntimeException if the calling thread has no Looper
- * @throws SecurityException if no suitable permission is present for the provider.
- * @deprecated use the {@link LocationRequest} class instead
+ * @throws SecurityException if no suitable permission is present
+ * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
*/
@Deprecated
public void requestLocationUpdates(String provider, long minTime, float minDistance,
@@ -512,63 +503,11 @@
}
/**
- * Registers the current activity to be notified periodically by
- * the named provider. Periodically, the supplied LocationListener will
- * be called with the current Location or with status updates.
+ * Register for location updates using the named provider, and a callback on
+ * the specified looper thread.
*
- * <p> It may take a while to receive the first location update. If
- * an immediate location is required, applications may use the
- * {@link #getLastKnownLocation(String)} method.
- *
- * <p> In case the provider is disabled by the user, updates will stop,
- * and the {@link LocationListener#onProviderDisabled(String)}
- * method will be called. As soon as the provider is enabled again,
- * the {@link LocationListener#onProviderEnabled(String)} method will
- * be called and location updates will start again.
- *
- * <p> The update interval can be controlled using the minTime parameter.
- * The elapsed time between location updates will never be less than
- * minTime, although it can be more depending on the Location Provider
- * implementation and the update interval requested by other applications.
- *
- * <p> Choosing a sensible value for minTime is important to conserve
- * battery life. Each location update requires power from
- * GPS, WIFI, Cell and other radios. Select a minTime value as high as
- * possible while still providing a reasonable user experience.
- * If your application is not in the foreground and showing
- * location to the user then your application should avoid using an active
- * provider (such as {@link #NETWORK_PROVIDER} or {@link #GPS_PROVIDER}),
- * but if you insist then select a minTime of 5 * 60 * 1000 (5 minutes)
- * or greater. If your application is in the foreground and showing
- * location to the user then it is appropriate to select a faster
- * update interval.
- *
- * <p> The minDistance parameter can also be used to control the
- * frequency of location updates. If it is greater than 0 then the
- * location provider will only send your application an update when
- * the location has changed by at least minDistance meters, AND
- * at least minTime milliseconds have passed. However it is more
- * difficult for location providers to save power using the minDistance
- * parameter, so minTime should be the primary tool to conserving battery
- * life.
- *
- * <p> If your application wants to passively observe location
- * updates triggered by other applications, but not consume
- * any additional power otherwise, then use the {@link #PASSIVE_PROVIDER}
- * This provider does not actively turn on or modify active location
- * providers, so you do not need to be as careful about minTime and
- * minDistance. However if your application performs heavy work
- * on a location update (such as network activity) then you should
- * select non-zero values for minTime and/or minDistance to rate-limit
- * your update frequency in the case another application enables a
- * location provider with extremely fast updates.
- *
- * <p> The supplied Looper is used to implement the callback mechanism.
- *
- * <p class="note"> Prior to Jellybean, the minTime parameter was
- * only a hint, and some location provider implementations ignored it.
- * From Jellybean and onwards it is mandatory for Android compatible
- * devices to observe both the minTime and minDistance parameters.
+ * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
+ * for more detail on how to use this (deprecated) method.
*
* @param provider the name of the provider with which to register
* @param minTime minimum time interval between location updates, in milliseconds
@@ -577,13 +516,14 @@
* {@link LocationListener#onLocationChanged} method will be called for
* each location update
* @param looper a Looper object whose message queue will be used to
- * implement the callback mechanism, or null to make callbacks on the
- * main thread
+ * implement the callback mechanism, or null to make callbacks on the calling
+ * thread
*
* @throws IllegalArgumentException if provider is null or doesn't exist
* @throws IllegalArgumentException if listener is null
- * @throws SecurityException if no suitable permission is present for the provider.
- * @deprecated use the {@link LocationRequest} class instead
+ * @throws SecurityException if no suitable permission is present
+ *
+ * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
*/
@Deprecated
public void requestLocationUpdates(String provider, long minTime, float minDistance,
@@ -597,52 +537,11 @@
}
/**
- * Registers the current activity to be notified periodically based on
- * the supplied criteria. Periodically, the supplied LocationListener will
- * be called with the current Location or with status updates.
+ * Register for location updates using a Criteria, and a callback
+ * on the specified looper thread.
*
- * <p> It may take a while to receive the first location update. If
- * an immediate location is required, applications may use the
- * {@link #getLastKnownLocation(String)} method.
- *
- * <p> In case the provider is disabled by the user, updates will stop,
- * and the {@link LocationListener#onProviderDisabled(String)}
- * method will be called. As soon as the provider is enabled again,
- * the {@link LocationListener#onProviderEnabled(String)} method will
- * be called and location updates will start again.
- *
- * <p> The update interval can be controlled using the minTime parameter.
- * The elapsed time between location updates will never be less than
- * minTime, although it can be more depending on the Location Provider
- * implementation and the update interval requested by other applications.
- *
- * <p> Choosing a sensible value for minTime is important to conserve
- * battery life. Each location update requires power from
- * GPS, WIFI, Cell and other radios. Select a minTime value as high as
- * possible while still providing a reasonable user experience.
- * If your application is not in the foreground and showing
- * location to the user then your application should avoid using an active
- * provider (such as {@link #NETWORK_PROVIDER} or {@link #GPS_PROVIDER}),
- * but if you insist then select a minTime of 5 * 60 * 1000 (5 minutes)
- * or greater. If your application is in the foreground and showing
- * location to the user then it is appropriate to select a faster
- * update interval.
- *
- * <p> The minDistance parameter can also be used to control the
- * frequency of location updates. If it is greater than 0 then the
- * location provider will only send your application an update when
- * the location has changed by at least minDistance meters, AND
- * at least minTime milliseconds have passed. However it is more
- * difficult for location providers to save power using the minDistance
- * parameter, so minTime should be the primary tool to conserving battery
- * life.
- *
- * <p> The supplied Looper is used to implement the callback mechanism.
- *
- * <p class="note"> Prior to Jellybean, the minTime parameter was
- * only a hint, and some location provider implementations ignored it.
- * From Jellybean and onwards it is mandatory for Android compatible
- * devices to observe both the minTime and minDistance parameters.
+ * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
+ * for more detail on how to use this (deprecated) method.
*
* @param minTime minimum time interval between location updates, in milliseconds
* @param minDistance minimum distance between location updates, in meters
@@ -652,13 +551,14 @@
* {@link LocationListener#onLocationChanged} method will be called for
* each location update
* @param looper a Looper object whose message queue will be used to
- * implement the callback mechanism, or null to make callbacks on the
- * main thread.
+ * implement the callback mechanism, or null to make callbacks on the calling
+ * thread
*
* @throws IllegalArgumentException if criteria is null
* @throws IllegalArgumentException if listener is null
- * @throws SecurityException if no suitable permission is present for the provider.
- * @deprecated use the {@link LocationRequest} class instead
+ * @throws SecurityException if no suitable permission is present
+ *
+ * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
*/
@Deprecated
public void requestLocationUpdates(long minTime, float minDistance, Criteria criteria,
@@ -672,70 +572,11 @@
}
/**
- * Registers the current activity to be notified periodically by
- * the named provider. Periodically, the supplied PendingIntent will
- * be broadcast with the current Location or with status updates.
+ * Register for location updates using the named provider, and a
+ * pending intent.
*
- * <p> Location updates are sent with a key of
- * {@link #KEY_LOCATION_CHANGED} and a {@link android.location.Location} value.
- *
- * <p> It may take a while to receive the first location update. If
- * an immediate location is required, applications may use the
- * {@link #getLastKnownLocation(String)} method.
- *
- * <p> The update interval can be controlled using the minTime parameter.
- * The elapsed time between location updates will never be less than
- * minTime, although it can be more depending on the Location Provider
- * implementation and the update interval requested by other applications.
- *
- * <p> Choosing a sensible value for minTime is important to conserve
- * battery life. Each location update requires power from
- * GPS, WIFI, Cell and other radios. Select a minTime value as high as
- * possible while still providing a reasonable user experience.
- * If your application is not in the foreground and showing
- * location to the user then your application should avoid using an active
- * provider (such as {@link #NETWORK_PROVIDER} or {@link #GPS_PROVIDER}),
- * but if you insist then select a minTime of 5 * 60 * 1000 (5 minutes)
- * or greater. If your application is in the foreground and showing
- * location to the user then it is appropriate to select a faster
- * update interval.
- *
- * <p> The minDistance parameter can also be used to control the
- * frequency of location updates. If it is greater than 0 then the
- * location provider will only send your application an update when
- * the location has changed by at least minDistance meters, AND
- * at least minTime milliseconds have passed. However it is more
- * difficult for location providers to save power using the minDistance
- * parameter, so minTime should be the primary tool to conserving battery
- * life.
- *
- * <p> If your application wants to passively observe location
- * updates triggered by other applications, but not consume
- * any additional power otherwise, then use the {@link #PASSIVE_PROVIDER}
- * This provider does not actively turn on or modify active location
- * providers, so you do not need to be as careful about minTime and
- * minDistance. However if your application performs heavy work
- * on a location update (such as network activity) then you should
- * select non-zero values for minTime and/or minDistance to rate-limit
- * your update frequency in the case another application enables a
- * location provider with extremely fast updates.
- *
- * <p> If the provider is disabled by the user, updates will stop,
- * and an intent will be sent with an extra with key
- * {@link #KEY_PROVIDER_ENABLED} and a boolean value of false.
- * If the provider is re-enabled, an intent will be sent with an
- * extra with key {@link #KEY_PROVIDER_ENABLED} and a boolean value of
- * true and location updates will start again.
- *
- * <p> If the provider's status changes, an intent will be sent with
- * an extra with key {@link #KEY_STATUS_CHANGED} and an integer value
- * indicating the new status. Any extras associated with the status
- * update will be sent as well.
- *
- * <p class="note"> Prior to Jellybean, the minTime parameter was
- * only a hint, and some location provider implementations ignored it.
- * From Jellybean and onwards it is mandatory for Android compatible
- * devices to observe both the minTime and minDistance parameters.
+ * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
+ * for more detail on how to use this (deprecated) method.
*
* @param provider the name of the provider with which to register
* @param minTime minimum time interval between location updates, in milliseconds
@@ -745,8 +586,9 @@
* @throws IllegalArgumentException if provider is null or doesn't exist
* on this device
* @throws IllegalArgumentException if intent is null
- * @throws SecurityException if no suitable permission is present for the provider.
- * @deprecated use the {@link LocationRequest} class instead
+ * @throws SecurityException if no suitable permission is present
+ *
+ * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
*/
@Deprecated
public void requestLocationUpdates(String provider, long minTime, float minDistance,
@@ -760,18 +602,28 @@
}
/**
- * Registers the current activity to be notified periodically based on
- * the supplied criteria. Periodically, the supplied PendingIntent will
- * be broadcast with the current Location or with status updates.
+ * Register for location updates using a Criteria and pending intent.
*
- * <p> Location updates are sent with a key of
- * {@link #KEY_LOCATION_CHANGED} and a {@link android.location.Location} value.
+ * <p>The <code>requestLocationUpdates()</code> and
+ * <code>requestSingleUpdate()</code> methods involving
+ * an explicit String provider or {@link Criteria} are deprecated.
+ *
+ * <p>They register the current activity to be updated
+ * periodically by the named provider, or by the provider matching
+ * the specified {@link Criteria}, with location and status updates.
*
* <p> It may take a while to receive the first location update. If
* an immediate location is required, applications may use the
* {@link #getLastKnownLocation(String)} method.
*
- * <p> The update interval can be controlled using the minTime parameter.
+ * <p> Location updates are received either by {@link LocationListener}
+ * callbacks, or by broadcast intents to a supplied {@link PendingIntent}.
+ *
+ * <p> If the caller supplied a pending intent, then location updates
+ * are sent with a key of {@link #KEY_LOCATION_CHANGED} and a
+ * {@link android.location.Location} value.
+ *
+ * <p> The location update interval can be controlled using the minTime parameter.
* The elapsed time between location updates will never be less than
* minTime, although it can be more depending on the Location Provider
* implementation and the update interval requested by other applications.
@@ -797,17 +649,36 @@
* parameter, so minTime should be the primary tool to conserving battery
* life.
*
- * <p> If the provider is disabled by the user, updates will stop,
- * and an intent will be sent with an extra with key
- * {@link #KEY_PROVIDER_ENABLED} and a boolean value of false.
- * If the provider is re-enabled, an intent will be sent with an
- * extra with key {@link #KEY_PROVIDER_ENABLED} and a boolean value of
- * true and location updates will start again.
+ * <p> If your application wants to passively observe location
+ * updates triggered by other applications, but not consume
+ * any additional power otherwise, then use the {@link #PASSIVE_PROVIDER}
+ * This provider does not actively turn on or modify active location
+ * providers, so you do not need to be as careful about minTime and
+ * minDistance. However if your application performs heavy work
+ * on a location update (such as network activity) then you should
+ * select non-zero values for minTime and/or minDistance to rate-limit
+ * your update frequency in the case another application enables a
+ * location provider with extremely fast updates.
*
- * <p> If the provider's status changes, an intent will be sent with
- * an extra with key {@link #KEY_STATUS_CHANGED} and an integer value
- * indicating the new status. Any extras associated with the status
- * update will be sent as well.
+ * <p>In case the provider is disabled by the user, updates will stop,
+ * and a provider availability update will be sent.
+ * As soon as the provider is enabled again,
+ * location updates will immediately resume and a provider availability
+ * update sent. Providers can also send status updates, at any time,
+ * with extra's specific to the provider. If a callback was supplied
+ * then status and availability updates are via
+ * {@link LocationListener#onProviderDisabled},
+ * {@link LocationListener#onProviderEnabled} or
+ * {@link LocationListener#onStatusChanged}. Alternately, if a
+ * pending intent was supplied then status and availability updates
+ * are broadcast intents with extra keys of
+ * {@link #KEY_PROVIDER_ENABLED} or {@link #KEY_STATUS_CHANGED}.
+ *
+ * <p> If a {@link LocationListener} is used but with no Looper specified
+ * then the calling thread must already
+ * be a {@link android.os.Looper} thread such as the main thread of the
+ * calling Activity. If a Looper is specified with a {@link LocationListener}
+ * then callbacks are made on the supplied Looper thread.
*
* <p class="note"> Prior to Jellybean, the minTime parameter was
* only a hint, and some location provider implementations ignored it.
@@ -822,8 +693,9 @@
*
* @throws IllegalArgumentException if criteria is null
* @throws IllegalArgumentException if intent is null
- * @throws SecurityException if no suitable permission is present for the provider.
- * @deprecated use the {@link LocationRequest} class instead
+ * @throws SecurityException if no suitable permission is present
+ *
+ * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
*/
@Deprecated
public void requestLocationUpdates(long minTime, float minDistance, Criteria criteria,
@@ -837,32 +709,25 @@
}
/**
- * Requests a single location update from the named provider.
+ * Register for a single location update using the named provider and
+ * a callback.
*
- * <p> It may take a while to receive the most recent location. If
- * an immediate location is required, applications may use the
- * {@link #getLastKnownLocation(String)} method.
- *
- * <p> In case the provider is disabled by the user, the update will not be received,
- * and the {@link LocationListener#onProviderDisabled(String)}
- * method will be called. As soon as the provider is enabled again,
- * the {@link LocationListener#onProviderEnabled(String)} method will
- * be called and location updates will start again.
- *
- * <p> The supplied Looper is used to implement the callback mechanism.
+ * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
+ * for more detail on how to use this (deprecated) method.
*
* @param provider the name of the provider with which to register
* @param listener a {#link LocationListener} whose
* {@link LocationListener#onLocationChanged} method will be called when
* the location update is available
* @param looper a Looper object whose message queue will be used to
- * implement the callback mechanism, or null to make callbacks on the
- * main thread
+ * implement the callback mechanism, or null to make callbacks on the calling
+ * thread
*
* @throws IllegalArgumentException if provider is null or doesn't exist
* @throws IllegalArgumentException if listener is null
- * @throws SecurityException if no suitable permission is present for the provider
- * @deprecated use the {@link LocationRequest} class instead
+ * @throws SecurityException if no suitable permission is present
+ *
+ * @deprecated Use {@link LocationRequest#setNumUpdates} instead
*/
@Deprecated
public void requestSingleUpdate(String provider, LocationListener listener, Looper looper) {
@@ -875,19 +740,11 @@
}
/**
- * Requests a single location update based on the specified criteria.
+ * Register for a single location update using a Criteria and
+ * a callback.
*
- * <p> It may take a while to receive the most recent location. If
- * an immediate location is required, applications may use the
- * {@link #getLastKnownLocation(String)} method.
- *
- * <p> In case the provider is disabled by the user, the update will not be received,
- * and the {@link LocationListener#onProviderDisabled(String)}
- * method will be called. As soon as the provider is enabled again,
- * the {@link LocationListener#onProviderEnabled(String)} method will
- * be called and location updates will start again.
- *
- * <p> The supplied Looper is used to implement the callback mechanism.
+ * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
+ * for more detail on how to use this (deprecated) method.
*
* @param criteria contains parameters for the location manager to choose the
* appropriate provider and parameters to compute the location
@@ -895,14 +752,14 @@
* {@link LocationListener#onLocationChanged} method will be called when
* the location update is available
* @param looper a Looper object whose message queue will be used to
- * implement the callback mechanism, or null to make callbacks on the
- * main thread
+ * implement the callback mechanism, or null to make callbacks on the calling
+ * thread
*
* @throws IllegalArgumentException if criteria is null
* @throws IllegalArgumentException if listener is null
- * @throws SecurityException if no suitable permission is present to access
- * the location services
- * @deprecated use the {@link LocationRequest} class instead
+ * @throws SecurityException if no suitable permission is present
+ *
+ * @deprecated Use {@link LocationRequest#setNumUpdates} instead
*/
@Deprecated
public void requestSingleUpdate(Criteria criteria, LocationListener listener, Looper looper) {
@@ -915,28 +772,19 @@
}
/**
- * Requests a single location update from the named provider.
+ * Register for a single location update using a named provider and pending intent.
*
- * <p> It may take a while to receive the most recent location. If
- * an immediate location is required, applications may use the
- * {@link #getLastKnownLocation(String)} method.
- *
- * <p> Location updates are sent with a key of
- * {@link #KEY_LOCATION_CHANGED} and a {@link android.location.Location} value.
- *
- * <p> In case the provider is disabled by the user, the update will not be received,
- * and the {@link LocationListener#onProviderDisabled(String)}
- * method will be called. As soon as the provider is enabled again,
- * the {@link LocationListener#onProviderEnabled(String)} method will
- * be called and location updates will start again.
+ * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
+ * for more detail on how to use this (deprecated) method.
*
* @param provider the name of the provider with which to register
* @param intent a {#link PendingIntent} to be sent for the location update
*
* @throws IllegalArgumentException if provider is null or doesn't exist
* @throws IllegalArgumentException if intent is null
- * @throws SecurityException if no suitable permission is present for the provider
- * @deprecated use the {@link LocationRequest} class instead
+ * @throws SecurityException if no suitable permission is present
+ *
+ * @deprecated Use {@link LocationRequest#setNumUpdates} instead
*/
@Deprecated
public void requestSingleUpdate(String provider, PendingIntent intent) {
@@ -949,21 +797,10 @@
}
/**
- * Requests a single location update based on the specified criteria.
+ * Register for a single location update using a Criteria and pending intent.
*
- * <p> It may take a while to receive the most recent location. If
- * an immediate location is required, applications may use the
- * {@link #getLastKnownLocation(String)} method.
- *
- * <p> Location updates are sent with a key of
- * {@link #KEY_LOCATION_CHANGED} and a {@link android.location.Location} value.
- *
- * <p> If the provider is disabled by the user, an update will not be
- * received, and an intent will be sent with an extra with key
- * {@link #KEY_PROVIDER_ENABLED} and a boolean value of false.
- * If the provider is re-enabled, an intent will be sent with an
- * extra with key {@link #KEY_PROVIDER_ENABLED} and a boolean value of
- * true and the location update will occur.
+ * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
+ * for more detail on how to use this (deprecated) method.
*
* @param criteria contains parameters for the location manager to choose the
* appropriate provider and parameters to compute the location
@@ -971,8 +808,9 @@
*
* @throws IllegalArgumentException if provider is null or doesn't exist
* @throws IllegalArgumentException if intent is null
- * @throws SecurityException if no suitable permission is present for the provider
- * @deprecated use the {@link LocationRequest} class instead
+ * @throws SecurityException if no suitable permission is present
+ *
+ * @deprecated Use {@link LocationRequest#setNumUpdates} instead
*/
@Deprecated
public void requestSingleUpdate(Criteria criteria, PendingIntent intent) {
@@ -984,12 +822,74 @@
requestLocationUpdates(request, null, null, intent);
}
+ /**
+ * Register for fused location updates using a LocationRequest and callback.
+ *
+ * <p>The system will automatically select and enable the best providers
+ * to compute a location for your application. It may use only passive
+ * locations, or just a single location source, or it may fuse together
+ * multiple location sources in order to produce the best possible
+ * result, depending on the quality of service requested in the
+ * {@link LocationRequest}.
+ *
+ * <p>LocationRequest can be null, in which case the system will choose
+ * default, low power parameters for location updates. You will occasionally
+ * receive location updates as available, without a major power impact on the
+ * system. If your application just needs an occasional location update
+ * without any strict demands, then pass a null LocationRequest.
+ *
+ * <p>Only one LocationRequest can be registered for each unique callback
+ * or pending intent. So a subsequent request with the same callback or
+ * pending intent will over-write the previous LocationRequest.
+ *
+ * <p> If a pending intent is supplied then location updates
+ * are sent with a key of {@link #KEY_LOCATION_CHANGED} and a
+ * {@link android.location.Location} value. If a callback is supplied
+ * then location updates are made using the
+ * {@link LocationListener#onLocationChanged} callback, on the specified
+ * Looper thread. If a {@link LocationListener} is used
+ * but with a null Looper then the calling thread must already
+ * be a {@link android.os.Looper} thread (such as the main thread) and
+ * callbacks will occur on this thread.
+ *
+ * <p> Provider status updates and availability updates are deprecated
+ * because the system is performing provider fusion on the applications
+ * behalf. So {@link LocationListener#onProviderDisabled},
+ * {@link LocationListener#onProviderEnabled}, {@link LocationListener#onStatusChanged}
+ * will not be called, and intents with extra keys of
+ * {@link #KEY_PROVIDER_ENABLED} or {@link #KEY_STATUS_CHANGED} will not
+ * be received.
+ *
+ * @param request quality of service required, null for default low power
+ * @param listener a {#link LocationListener} whose
+ * {@link LocationListener#onLocationChanged} method will be called when
+ * the location update is available
+ * @param looper a Looper object whose message queue will be used to
+ * implement the callback mechanism, or null to make callbacks on the calling
+ * thread
+ *
+ * @throws IllegalArgumentException if listener is null
+ * @throws SecurityException if no suitable permission is present
+ */
public void requestLocationUpdates(LocationRequest request, LocationListener listener,
Looper looper) {
checkListener(listener);
requestLocationUpdates(request, listener, looper, null);
}
+
+ /**
+ * Register for fused location updates using a LocationRequest and a pending intent.
+ *
+ * <p> See {@link #requestLocationUpdates(LocationRequest, LocationListener, Looper)}
+ * for more detail.
+ *
+ * @param request quality of service required, null for default low power
+ * @param intent a {#link PendingIntent} to be sent for the location update
+ *
+ * @throws IllegalArgumentException if intent is null
+ * @throws SecurityException if no suitable permission is present
+ */
public void requestLocationUpdates(LocationRequest request, PendingIntent intent) {
checkPendingIntent(intent);
requestLocationUpdates(request, null, null, intent);
@@ -1023,11 +923,12 @@
}
/**
- * Removes any current registration for location updates of the current activity
- * with the given LocationListener. Following this call, updates will no longer
+ * Removes all location updates for the specified LocationListener.
+ *
+ * <p>Following this call, updates will no longer
* occur for this listener.
*
- * @param listener {#link LocationListener} object that no longer needs location updates
+ * @param listener listener object that no longer needs location updates
* @throws IllegalArgumentException if listener is null
*/
public void removeUpdates(LocationListener listener) {
@@ -1048,11 +949,11 @@
}
/**
- * Removes any current registration for location updates of the current activity
- * with the given PendingIntent. Following this call, updates will no longer
- * occur for this intent.
+ * Removes all location updates for the specified pending intent.
*
- * @param intent {#link PendingIntent} object that no longer needs location updates
+ * <p>Following this call, updates will no longer for this pending intent.
+ *
+ * @param intent pending intent object that no longer needs location updates
* @throws IllegalArgumentException if intent is null
*/
public void removeUpdates(PendingIntent intent) {
@@ -1067,8 +968,10 @@
}
/**
- * Sets a proximity alert for the location given by the position
- * (latitude, longitude) and the given radius. When the device
+ * Set a proximity alert for the location given by the position
+ * (latitude, longitude) and the given radius.
+ *
+ * <p> When the device
* detects that it has entered or exited the area surrounding the
* location, the given PendingIntent will be used to create an Intent
* to be fired.
@@ -1088,10 +991,6 @@
* alert and no longer monitor it. A value of -1 indicates that
* there should be no expiration time.
*
- * <p> In case the screen goes to sleep, checks for proximity alerts
- * happen only once every 4 minutes. This conserves battery life by
- * ensuring that the device isn't perpetually awake.
- *
* <p> Internally, this method uses both {@link #NETWORK_PROVIDER}
* and {@link #GPS_PROVIDER}.
*
@@ -1106,9 +1005,9 @@
* @param intent a PendingIntent that will be used to generate an Intent to
* fire when entry to or exit from the alert region is detected
*
- * @throws SecurityException if no permission exists for the required
- * providers.
- * @deprecated use the {@link LocationRequest} class instead
+ * @throws SecurityException if no suitable permission is present
+ *
+ * @deprecated Use {@link LocationRequest} and {@link Geofence} instead
*/
@Deprecated
public void addProximityAlert(double latitude, double longitude, float radius, long expiration,
@@ -1125,7 +1024,40 @@
}
}
- public void requestGeofence(LocationRequest request, Geofence fence, PendingIntent intent) {
+ /**
+ * Add a geofence with the specified LocationRequest quality of service.
+ *
+ * <p> When the device
+ * detects that it has entered or exited the area surrounding the
+ * location, the given PendingIntent will be used to create an Intent
+ * to be fired.
+ *
+ * <p> The fired Intent will have a boolean extra added with key
+ * {@link #KEY_PROXIMITY_ENTERING}. If the value is true, the device is
+ * entering the proximity region; if false, it is exiting.
+ *
+ * <p> The geofence engine fuses results from all location providers to
+ * provide the best balance between accuracy and power. Applications
+ * can choose the quality of service required using the
+ * {@link LocationRequest} object. If it is null then a default,
+ * low power geo-fencing implementation is used. It is possible to cross
+ * a geo-fence without notification, but the system will do its best
+ * to detect, using {@link LocationRequest} as a hint to trade-off
+ * accuracy and power.
+ *
+ * <p> The power required by the geofence engine can depend on many factors,
+ * such as quality and interval requested in {@link LocationRequest},
+ * distance to nearest geofence and current device velocity.
+ *
+ * @param request quality of service required, null for default low power
+ * @param fence a geographical description of the geofence area
+ * @param intent pending intent to receive geofence updates
+ *
+ * @throws IllegalArgumentException if fence is null
+ * @throws IllegalArgumentException if intent is null
+ * @throws SecurityException if no suitable permission is present
+ */
+ public void addGeofence(LocationRequest request, Geofence fence, PendingIntent intent) {
checkPendingIntent(intent);
checkGeofence(fence);
@@ -1141,7 +1073,11 @@
*
* @param intent the PendingIntent that no longer needs to be notified of
* proximity alerts
- * @deprecated use the {@link LocationRequest} class instead
+ *
+ * @throws IllegalArgumentException if intent is null
+ * @throws SecurityException if no suitable permission is present
+ *
+ * @deprecated Use {@link LocationRequest} and {@link Geofence} instead
*/
@Deprecated
public void removeProximityAlert(PendingIntent intent) {
@@ -1155,6 +1091,19 @@
}
}
+ /**
+ * Remove a single geofence.
+ *
+ * <p>This removes only the specified geofence associated with the
+ * specified pending intent. All other geofences remain unchanged.
+ *
+ * @param fence a geofence previously passed to {@link #addGeofence}
+ * @param intent a pending intent previously passed to {@link #addGeofence}
+ *
+ * @throws IllegalArgumentException if fence is null
+ * @throws IllegalArgumentException if intent is null
+ * @throws SecurityException if no suitable permission is present
+ */
public void removeGeofence(Geofence fence, PendingIntent intent) {
checkPendingIntent(intent);
checkGeofence(fence);
@@ -1167,6 +1116,14 @@
}
}
+ /**
+ * Remove all geofences registered to the specified pending intent.
+ *
+ * @param intent a pending intent previously passed to {@link #addGeofence}
+ *
+ * @throws IllegalArgumentException if intent is null
+ * @throws SecurityException if no suitable permission is present
+ */
public void removeAllGeofences(PendingIntent intent) {
checkPendingIntent(intent);
String packageName = mContext.getPackageName();
@@ -1179,16 +1136,18 @@
}
/**
- * Returns the current enabled/disabled status of the given provider. If the
- * user has enabled this provider in the Settings menu, true is returned
- * otherwise false is returned
+ * Returns the current enabled/disabled status of the given provider.
+ *
+ * <p>If the user has enabled this provider in the Settings menu, true
+ * is returned otherwise false is returned
*
* @param provider the name of the provider
* @return true if the provider is enabled
*
- * @throws SecurityException if no suitable permission is present for the provider.
* @throws IllegalArgumentException if provider is null
- * @deprecated use the {@link LocationRequest} class instead
+ * @throws SecurityException if no suitable permission is present
+ *
+ * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
*/
@Deprecated
public boolean isProviderEnabled(String provider) {
@@ -1202,19 +1161,32 @@
}
}
- public Location getLastLocation(LocationRequest request) {
+ /**
+ * Get the last known location.
+ *
+ * <p>This location could be very old so use
+ * {@link Location#getElapsedRealtimeNano} to calculate its age. It can
+ * also return null if no previous location is available.
+ *
+ * <p>Always returns immediately.
+ *
+ * @return The last known location, or null if not available
+ * @throws SecurityException if no suitable permission is present
+ */
+ public Location getLastLocation() {
try {
- return mService.getLastLocation(request);
+ return mService.getLastLocation(null);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException", e);
return null;
}
}
-
/**
* Returns a Location indicating the data from the last known
- * location fix obtained from the given provider. This can be done
+ * location fix obtained from the given provider.
+ *
+ * <p> This can be done
* without starting the provider. Note that this location could
* be out-of-date, for example if the device was turned off and
* moved to another location.
@@ -1224,9 +1196,10 @@
* @param provider the name of the provider
* @return the last known location for the provider, or null
*
- * @throws SecurityException if no suitable permission is present for the provider.
+ * @throws SecurityException if no suitable permission is present
* @throws IllegalArgumentException if provider is null or doesn't exist
- * @deprecated use the {@link LocationRequest} class instead
+ *
+ * @deprecated Use {@link #getLastLocation} instead
*/
@Deprecated
public Location getLastKnownLocation(String provider) {
@@ -1243,27 +1216,21 @@
}
}
- // Mock provider support
+ // --- Mock provider support ---
+ // TODO: It would be fantastic to deprecate mock providers entirely, and replace
+ // with something closer to LocationProviderBase.java
/**
* Creates a mock location provider and adds it to the set of active providers.
*
* @param name the provider name
- * @param requiresNetwork
- * @param requiresSatellite
- * @param requiresCell
- * @param hasMonetaryCost
- * @param supportsAltitude
- * @param supportsSpeed
- * @param supportsBearing
- * @param powerRequirement
- * @param accuracy
*
* @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
* or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
* Settings.Secure.ALLOW_MOCK_LOCATION} system setting is not enabled
* @throws IllegalArgumentException if a provider with the given name already exists
- * @deprecated use the {@link LocationRequest} class instead
+ *
+ * @deprecated requesting location providers by name is deprecated
*/
@Deprecated
public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
@@ -1292,7 +1259,8 @@
* or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
* Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
* @throws IllegalArgumentException if no provider with the given name exists
- * @deprecated use the {@link LocationRequest} class instead
+ *
+ * @deprecated requesting location providers by name is deprecated
*/
@Deprecated
public void removeTestProvider(String provider) {
@@ -1318,7 +1286,8 @@
* Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
* @throws IllegalArgumentException if no provider with the given name exists
* @throws IllegalArgumentException if the location is incomplete
- * @deprecated use the {@link LocationRequest} class instead
+ *
+ * @deprecated requesting location providers by name is deprecated
*/
@Deprecated
public void setTestProviderLocation(String provider, Location loc) {
@@ -1351,7 +1320,8 @@
* or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
* Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
* @throws IllegalArgumentException if no provider with the given name exists
- * @deprecated use the {@link LocationRequest} class instead
+ *
+ * @deprecated requesting location providers by name is deprecated
*/
@Deprecated
public void clearTestProviderLocation(String provider) {
@@ -1373,7 +1343,8 @@
* or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
* Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
* @throws IllegalArgumentException if no provider with the given name exists
- * @deprecated use the {@link LocationRequest} class instead
+ *
+ * @deprecated requesting location providers by name is deprecated
*/
@Deprecated
public void setTestProviderEnabled(String provider, boolean enabled) {
@@ -1393,7 +1364,8 @@
* or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
* Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
* @throws IllegalArgumentException if no provider with the given name exists
- * @deprecated use the {@link LocationRequest} class instead
+ *
+ * @deprecated requesting location providers by name is deprecated
*/
@Deprecated
public void clearTestProviderEnabled(String provider) {
@@ -1417,7 +1389,8 @@
* or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
* Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
* @throws IllegalArgumentException if no provider with the given name exists
- * @deprecated use the {@link LocationRequest} class instead
+ *
+ * @deprecated requesting location providers by name is deprecated
*/
@Deprecated
public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
@@ -1437,7 +1410,8 @@
* or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
* Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
* @throws IllegalArgumentException if no provider with the given name exists
- * @deprecated use the {@link LocationRequest} class instead
+ *
+ * @deprecated requesting location providers by name is deprecated
*/
@Deprecated
public void clearTestProviderStatus(String provider) {
@@ -1448,7 +1422,7 @@
}
}
- // GPS-specific support
+ // --- GPS-specific support ---
// This class is used to send GPS status events to the client's main thread.
private class GpsStatusListenerTransport extends IGpsStatusListener.Stub {
@@ -1682,7 +1656,8 @@
* The provider may optionally fill the extras Bundle with results from the command.
*
* @return true if the command succeeds.
- * @deprecated use the {@link LocationRequest} class instead
+ *
+ * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
*/
@Deprecated
public boolean sendExtraCommand(String provider, String command, Bundle extras) {
@@ -1698,7 +1673,7 @@
* Used by NetInitiatedActivity to report user response
* for network initiated GPS fix requests.
*
- * {@hide}
+ * @hide
*/
public boolean sendNiResponse(int notifId, int userResponse) {
try {
@@ -1720,6 +1695,7 @@
throw new IllegalArgumentException("invalid criteria: " + criteria);
}
}
+
private static void checkListener(LocationListener listener) {
if (listener == null) {
throw new IllegalArgumentException("invalid listener: " + listener);
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index 3110196..b1863b8 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -21,24 +21,140 @@
import android.os.SystemClock;
import android.util.TimeUtils;
+
+/**
+ * A data object that contains quality of service parameters for requests
+ * to the {@link LocationManager}.
+ *
+ * <p>LocationRequest objects are used to request a quality of service
+ * for location updates from the Location Manager.
+ *
+ * <p>For example, if your application wants high accuracy location
+ * it should create a location request with {@link #setQuality} set to
+ * {@link #ACCURACY_FINE} or {@link #POWER_HIGH}, and it should set
+ * {@link #setInterval} to less than one second. This would be
+ * appropriate for mapping applications that are showing your location
+ * in real-time.
+ *
+ * <p>At the other extreme, if you want negligible power
+ * impact, but to still receive location updates when available, then use
+ * {@link #setQuality} with {@link #POWER_NONE}. With this request your
+ * application will not trigger (and therefore will not receive any
+ * power blame) any location updates, but will receive locations
+ * triggered by other applications. This would be appropriate for
+ * applications that have no firm requirement for location, but can
+ * take advantage when available.
+ *
+ * <p>In between these two extremes is a very common use-case, where
+ * applications definitely want to receive
+ * updates at a specified interval, and can receive them faster when
+ * available, but still want a low power impact. These applications
+ * should consider {@link #POWER_LOW} combined with a faster
+ * {@link #setFastestInterval} (such as 1 minute) and a slower
+ * {@link #setInterval} (such as 60 minutes). They will only be assigned
+ * power blame for the interval set by {@link #setInterval}, but can
+ * still receive locations triggered by other applications at a rate up
+ * to {@link #setFastestInterval}. This style of request is appropriate for
+ * many location aware applications, including background usage. Do be
+ * careful to also throttle {@link #setFastestInterval} if you perform
+ * heavy-weight work after receiving an update - such as using the network.
+ *
+ * <p>Activities should strongly consider removing all location
+ * request when entering the background
+ * (for example at {@link android.app.Activity#onPause}), or
+ * at least swap the request to a larger interval and lower quality.
+ * Future version of the location manager may automatically perform background
+ * throttling on behalf of applications.
+ *
+ * <p>Applications cannot specify the exact location sources that are
+ * used by Android's <em>Fusion Engine</em>. In fact, the system
+ * may have multiple location sources (providers) running and may
+ * fuse the results from several sources into a single Location object.
+ *
+ * <p>Location requests from applications with
+ * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} and not
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} will
+ * be automatically throttled to a slower interval, and the location
+ * object will be obfuscated to only show a coarse level of accuracy.
+ *
+ * <p>All location requests are considered hints, and you may receive
+ * locations that are more accurate, less accurate, and slower
+ * than requested.
+ */
public final class LocationRequest implements Parcelable {
- // QOS control
- public static final int ACCURACY_FINE = 100; // ~1 meter
- public static final int ACCURACY_BLOCK = 102; // ~100 meters
- public static final int ACCURACY_CITY = 104; // ~10 km
+ /**
+ * Used with {@link #setQuality} to request the most accurate locations available.
+ *
+ * <p>This may be up to 1 meter accuracy, although this is implementation dependent.
+ */
+ public static final int ACCURACY_FINE = 100;
+
+ /**
+ * Used with {@link #setQuality} to request "block" level accuracy.
+ *
+ * <p>Block level accuracy is considered to be about 100 meter accuracy,
+ * although this is implementation dependent. Using a coarse accuracy
+ * such as this often consumes less power.
+ */
+ public static final int ACCURACY_BLOCK = 102;
+
+ /**
+ * Used with {@link #setQuality} to request "city" level accuracy.
+ *
+ * <p>City level accuracy is considered to be about 10km accuracy,
+ * although this is implementation dependent. Using a coarse accuracy
+ * such as this often consumes less power.
+ */
+ public static final int ACCURACY_CITY = 104;
+
+ /**
+ * Used with {@link #setQuality} to require no direct power impact (passive locations).
+ *
+ * <p>This location request will not trigger any active location requests,
+ * but will receive locations triggered by other applications. Your application
+ * will not receive any direct power blame for location work.
+ */
public static final int POWER_NONE = 200;
+
+ /**
+ * Used with {@link #setQuality} to request low power impact.
+ *
+ * <p>This location request will avoid high power location work where
+ * possible.
+ */
public static final int POWER_LOW = 201;
+
+ /**
+ * Used with {@link #setQuality} to allow high power consumption for location.
+ *
+ * <p>This location request will allow high power location work.
+ */
public static final int POWER_HIGH = 203;
+ /**
+ * By default, mFastestInterval = FASTEST_INTERVAL_MULTIPLE * mInterval
+ */
+ private static final double FASTEST_INTERVAL_FACTOR = 6.0; // 6x
+
private int mQuality = POWER_LOW;
- private long mFastestInterval = 6 * 1000; // 6 seconds
- private long mInterval = 60 * 1000; // 1 minute
+ private long mInterval = 60 * 60 * 1000; // 60 minutes
+ private long mFastestInterval = (long)(mInterval / FASTEST_INTERVAL_FACTOR); // 10 minutes
+ private boolean mExplicitFastestInterval = false;
private long mExpireAt = Long.MAX_VALUE; // no expiry
private int mNumUpdates = Integer.MAX_VALUE; // no expiry
private float mSmallestDisplacement = 0.0f; // meters
private String mProvider = null; // for deprecated API's that explicitly request a provider
+ /**
+ * Create a location request with default parameters.
+ *
+ * <p>Default parameters are for a low power, slowly updated location.
+ * It can then be adjusted as required by the applications before passing
+ * to the {@link LocationManager}
+ *
+ * @return a new location request
+ */
public static LocationRequest create() {
LocationRequest request = new LocationRequest();
return request;
@@ -105,52 +221,217 @@
/** @hide */
public LocationRequest() { }
+ /**
+ * Set the quality of the request.
+ *
+ * <p>Use with a accuracy constant such as {@link #ACCURACY_FINE}, or a power
+ * constant such as {@link #POWER_LOW}. You cannot request both and accuracy and
+ * power, only one or the other can be specified. The system will then
+ * maximize accuracy or minimize power as appropriate.
+ *
+ * <p>The quality of the request is a strong hint to the system for which
+ * location sources to use. For example, {@link #ACCURACY_FINE} is more likely
+ * to use GPS, and {@link #POWER_LOW} is more likely to use WIFI & Cell tower
+ * positioning, but it also depends on many other factors (such as which sources
+ * are available) and is implementation dependent.
+ *
+ * <p>{@link #setQuality} and {@link #setInterval} are the most important parameters
+ * on a location request.
+ *
+ * @param quality an accuracy or power constant
+ * @throws InvalidArgumentException if the quality constant is not valid
+ * @return the same object, so that setters can be chained
+ */
public LocationRequest setQuality(int quality) {
checkQuality(quality);
mQuality = quality;
return this;
}
+ /**
+ * Get the quality of the request.
+ *
+ * @return an accuracy or power constant
+ */
public int getQuality() {
return mQuality;
}
+ /**
+ * Set the desired interval for active location updates, in milliseconds.
+ *
+ * <p>The location manager will actively try to obtain location updates
+ * for your application at this interval, so it has a
+ * direct influence on the amount of power used by your application.
+ * Choose your interval wisely.
+ *
+ * <p>This interval is inexact. You may not receive updates at all (if
+ * no location sources are available), or you may receive them
+ * slower than requested. You may also receive them faster than
+ * requested (if other applications are requesting location at a
+ * faster interval). The fastest rate that that you will receive
+ * updates can be controlled with {@link #setFastestInterval}.
+ *
+ * <p>Applications with only the coarse location permission may have their
+ * interval silently throttled.
+ *
+ * <p>An interval of 0 is allowed, but not recommended, since
+ * location updates may be extremely fast on future implementations.
+ *
+ * <p>{@link #setQuality} and {@link #setInterval} are the most important parameters
+ * on a location request.
+ *
+ * @param millis desired interval in millisecond, inexact
+ * @throws InvalidArgumentException if the interval is less than zero
+ * @return the same object, so that setters can be chained
+ */
public LocationRequest setInterval(long millis) {
checkInterval(millis);
mInterval = millis;
+ if (!mExplicitFastestInterval) {
+ mFastestInterval = (long)(mInterval / FASTEST_INTERVAL_FACTOR);
+ }
return this;
}
+ /**
+ * Get the desired interval of this request, in milliseconds.
+ *
+ * @return desired interval in milliseconds, inexact
+ */
public long getInterval() {
return mInterval;
}
+ /**
+ * Explicitly set the fastest interval for location updates, in
+ * milliseconds.
+ *
+ * <p>This controls the fastest rate at which your application will
+ * receive location updates, which might be faster than
+ * {@link #setInterval} in some situations (for example, if other
+ * applications are triggering location updates).
+ *
+ * <p>This allows your application to passively acquire locations
+ * at a rate faster than it actively acquires locations, saving power.
+ *
+ * <p>Unlike {@link #setInterval}, this parameter is exact. Your
+ * application will never receive updates faster than this value.
+ *
+ * <p>If you don't call this method, a fastest interval
+ * will be selected for you. It will be a value faster than your
+ * active interval ({@link #setInterval}).
+ *
+ * <p>An interval of 0 is allowed, but not recommended, since
+ * location updates may be extremely fast on future implementations.
+ *
+ * <p>If {@link #setFastestInterval} is set slower than {@link #setInterval},
+ * then your effective fastest interval is {@link #setInterval}.
+ *
+ * @param millis fastest interval for updates in milliseconds, exact
+ * @throws InvalidArgumentException if the interval is less than zero
+ * @return the same object, so that setters can be chained
+ */
public LocationRequest setFastestInterval(long millis) {
checkInterval(millis);
+ mExplicitFastestInterval = true;
mFastestInterval = millis;
return this;
}
+ /**
+ * Get the fastest interval of this request, in milliseconds.
+ *
+ * <p>The system will never provide location updates faster
+ * than the minimum of {@link #getFastestInterval} and
+ * {@link #getInterval}.
+ *
+ * @return fastest interval in milliseconds, exact
+ */
public long getFastestInterval() {
return mFastestInterval;
}
+ /**
+ * Set the duration of this request, in milliseconds.
+ *
+ * <p>The duration begins immediately (and not when the request
+ * is passed to the location manager), so call this method again
+ * if the request is re-used at a later time.
+ *
+ * <p>The location manager will automatically stop updates after
+ * the request expires.
+ *
+ * <p>The duration includes suspend time. Values less than 0
+ * are allowed, but indicate that the request has already expired.
+ *
+ * @param millis duration of request in milliseconds
+ * @return the same object, so that setters can be chained
+ */
public LocationRequest setExpireIn(long millis) {
mExpireAt = millis + SystemClock.elapsedRealtime();
if (mExpireAt < 0) mExpireAt = 0;
return this;
}
+ /**
+ * Set the request expiration time, in millisecond since boot.
+ *
+ * <p>This expiration time uses the same time base as {@link SystemClock#elapsedRealtime}.
+ *
+ * <p>The location manager will automatically stop updates after
+ * the request expires.
+ *
+ * <p>The duration includes suspend time. Values before {@link SystemClock#elapsedRealtime}
+ * are allowed, but indicate that the request has already expired.
+ *
+ * @param millis expiration time of request, in milliseconds since boot including suspend
+ * @return the same object, so that setters can be chained
+ */
public LocationRequest setExpireAt(long millis) {
mExpireAt = millis;
if (mExpireAt < 0) mExpireAt = 0;
return this;
}
+ /**
+ * Get the request expiration time, in milliseconds since boot.
+ *
+ * <p>This value can be compared to {@link SystemClock#elapsedRealtime} to determine
+ * the time until expiration.
+ *
+ * @return expiration time of request, in milliseconds since boot including suspend
+ */
public long getExpireAt() {
return mExpireAt;
}
+ /**
+ * Set the number of location updates.
+ *
+ * <p>By default locations are continuously updated until the request is explicitly
+ * removed, however you can optionally request a set number of updates.
+ * For example, if your application only needs a single fresh location,
+ * then call this method with a value of 1 before passing the request
+ * to the location manager.
+ *
+ * @param numUpdates the number of location updates requested
+ * @throws InvalidArgumentException if numUpdates is 0 or less
+ * @return the same object, so that setters can be chained
+ */
+ public LocationRequest setNumUpdates(int numUpdates) {
+ if (numUpdates <= 0) throw new IllegalArgumentException("invalid numUpdates: " + numUpdates);
+ mNumUpdates = numUpdates;
+ return this;
+ }
+
+ /**
+ * Get the number of updates requested.
+ *
+ * <p>By default this is {@link Integer#MAX_VALUE}, which indicates that
+ * locations are updated until the request is explicitly removed.
+ * @return number of updates
+ */
public int getNumUpdates() {
return mNumUpdates;
}
@@ -165,11 +446,6 @@
}
}
- public LocationRequest setNumUpdates(int numUpdates) {
- if (numUpdates < 0) throw new IllegalArgumentException("invalid numUpdates: " + numUpdates);
- mNumUpdates = numUpdates;
- return this;
- }
/** @hide */
public LocationRequest setProvider(String provider) {
@@ -195,20 +471,6 @@
return mSmallestDisplacement;
}
- /** @hide */
- public LocationRequest applyCoarsePermissionRestrictions() {
- switch (mQuality) {
- case ACCURACY_FINE:
- mQuality = ACCURACY_BLOCK;
- break;
- }
- // cap fastest interval to 6 seconds
- if (mFastestInterval < 6 * 1000) mFastestInterval = 6 * 1000;
- // cap requested interval to 1 minute
- if (mInterval < 60 * 1000) mInterval = 60 * 1000;
- return this;
- }
-
private static void checkInterval(long millis) {
if (millis < 0) {
throw new IllegalArgumentException("invalid interval: " + millis);
@@ -261,10 +523,12 @@
return new LocationRequest[size];
}
};
+
@Override
public int describeContents() {
return 0;
}
+
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(mQuality);
diff --git a/location/lib/README.txt b/location/lib/README.txt
new file mode 100644
index 0000000..400a7dd
--- /dev/null
+++ b/location/lib/README.txt
@@ -0,0 +1,30 @@
+This library (com.android.location.provider.jar) is a shared java library
+containing classes required by unbundled location providers.
+
+--- Rules of this library ---
+o This library is effectively a PUBLIC API for unbundled location providers
+ that may be distributed outside the system image. So it MUST BE API STABLE.
+ You can add but not remove. The rules are the same as for the
+ public platform SDK API.
+o This library can see and instantiate internal platform classes (such as
+ ProviderRequest.java), but it must not expose them in any public method
+ (or by extending them via inheritance). This would break clients of the
+ library because they cannot see the internal platform classes.
+
+This library is distributed in the system image, and loaded as
+a shared library. So you can change the implementation, but not
+the interface. In this way it is like framework.jar.
+
+--- Why does this library exists? ---
+
+Unbundled location providers (such as the NetworkLocationProvider)
+can not use internal platform classes.
+
+So ideally all of these classes would be part of the public platform SDK API,
+but that doesn't seem like a great idea when only applications with a special
+signature can implement this API.
+
+The compromise is this library.
+
+It wraps internal platform classes (like ProviderRequest) with a stable
+API that does not leak the internal classes.
diff --git a/location/lib/java/com/android/location/provider/GeocodeProvider.java b/location/lib/java/com/android/location/provider/GeocodeProvider.java
index 666bb02..d7a34af 100644
--- a/location/lib/java/com/android/location/provider/GeocodeProvider.java
+++ b/location/lib/java/com/android/location/provider/GeocodeProvider.java
@@ -25,12 +25,14 @@
import java.util.List;
/**
- * An abstract superclass for geocode providers that are implemented
- * outside of the core android platform.
- * Geocode providers can be implemented as services and return the result of
+ * Base class for geocode providers implemented as unbundled services.
+ *
+ * <p>Geocode providers can be implemented as services and return the result of
* {@link GeocodeProvider#getBinder()} in its getBinder() method.
*
- * @hide
+ * <p>IMPORTANT: This class is effectively a public API for unbundled
+ * applications, and must remain API stable. See README.txt in the root
+ * of this package for more information.
*/
public abstract class GeocodeProvider {
diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java
index 53b0cae..b0e5d2c 100644
--- a/location/lib/java/com/android/location/provider/LocationProviderBase.java
+++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java
@@ -34,10 +34,22 @@
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
-
/**
- * Base class for location providers implemented as services.
- * @hide
+ * Base class for location providers implemented as unbundled services.
+ *
+ * <p>The network location provider must export a service with action
+ * "com.android.location.service.v2.NetworkLocationProvider"
+ * and a valid minor version in a meta-data field on the service, and
+ * then return the result of {@link #getBinder()} on service binding.
+ *
+ * <p>The fused location provider must export a service with action
+ * "com.android.location.service.FusedLocationProvider"
+ * and a valid minor version in a meta-data field on the service, and
+ * then return the result of {@link #getBinder()} on service binding.
+ *
+ * <p>IMPORTANT: This class is effectively a public API for unbundled
+ * applications, and must remain API stable. See README.txt in the root
+ * of this package for more information.
*/
public abstract class LocationProviderBase {
private final String TAG;
diff --git a/location/lib/java/com/android/location/provider/ProviderPropertiesUnbundled.java b/location/lib/java/com/android/location/provider/ProviderPropertiesUnbundled.java
index c8bdda4..9ee4df21 100644
--- a/location/lib/java/com/android/location/provider/ProviderPropertiesUnbundled.java
+++ b/location/lib/java/com/android/location/provider/ProviderPropertiesUnbundled.java
@@ -19,9 +19,11 @@
import com.android.internal.location.ProviderProperties;
/**
- * This class is a public API for unbundled providers,
- * that hides the (hidden framework) ProviderProperties.
- * <p>Do _not_ remove public methods on this class.
+ * This class is an interface to Provider Properties for unbundled applications.
+ *
+ * <p>IMPORTANT: This class is effectively a public API for unbundled
+ * applications, and must remain API stable. See README.txt in the root
+ * of this package for more information.
*/
public final class ProviderPropertiesUnbundled {
private final ProviderProperties mProperties;
diff --git a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
index 3ff19ca..3605381 100644
--- a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
+++ b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
@@ -23,9 +23,11 @@
import com.android.internal.location.ProviderRequest;
/**
- * This class is a public API for unbundled providers,
- * that hides the (hidden framework) ProviderRequest.
- * <p>Do _not_ remove public methods on this class.
+ * This class is an interface to Provider Requests for unbundled applications.
+ *
+ * <p>IMPORTANT: This class is effectively a public API for unbundled
+ * applications, and must remain API stable. See README.txt in the root
+ * of this package for more information.
*/
public final class ProviderRequestUnbundled {
private final ProviderRequest mRequest;
diff --git a/media/libdrm/mobile1/src/objmng/drm_api.c b/media/libdrm/mobile1/src/objmng/drm_api.c
index 249cdbe..232d9f4 100644
--- a/media/libdrm/mobile1/src/objmng/drm_api.c
+++ b/media/libdrm/mobile1/src/objmng/drm_api.c
@@ -1478,13 +1478,13 @@
if (NULL != s->readBuf && s->readBufLen > 0) { /* read from backup buffer */
if (leftLen <= s->readBufLen) {
- memcpy(mediaBuf, s->readBuf + s->readBufOff, leftLen);
+ memcpy(mediaBuf + readBytes, s->readBuf + s->readBufOff, leftLen);
s->readBufOff += leftLen;
s->readBufLen -= leftLen;
readBytes += leftLen;
leftLen = 0;
} else {
- memcpy(mediaBuf, s->readBuf + s->readBufOff, s->readBufLen);
+ memcpy(mediaBuf + readBytes, s->readBuf + s->readBufOff, s->readBufLen);
s->readBufOff += s->readBufLen;
leftLen -= s->readBufLen;
readBytes += s->readBufLen;
diff --git a/opengl/java/com/google/android/gles_jni/GLImpl.java b/opengl/java/com/google/android/gles_jni/GLImpl.java
index 07f9e91..6b23be9 100644
--- a/opengl/java/com/google/android/gles_jni/GLImpl.java
+++ b/opengl/java/com/google/android/gles_jni/GLImpl.java
@@ -23,7 +23,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.os.Build;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.Log;
import java.nio.Buffer;
@@ -68,7 +68,7 @@
int version = 0;
IPackageManager pm = AppGlobals.getPackageManager();
try {
- ApplicationInfo applicationInfo = pm.getApplicationInfo(appName, 0, UserId.myUserId());
+ ApplicationInfo applicationInfo = pm.getApplicationInfo(appName, 0, UserHandle.myUserId());
if (applicationInfo != null) {
version = applicationInfo.targetSdkVersion;
}
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java b/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java
index 38a6091..1c22c7a 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java
@@ -22,7 +22,6 @@
import com.android.location.provider.ProviderRequestUnbundled;
-
import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 4552a55..1481eb2 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -38,7 +38,6 @@
import android.util.Log;
import com.android.internal.content.PackageHelper;
-import com.android.internal.telephony.BaseCommands;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.RILConstants;
@@ -65,7 +64,7 @@
// database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
// is properly propagated through your change. Not doing so will result in a loss of user
// settings.
- private static final int DATABASE_VERSION = 80;
+ private static final int DATABASE_VERSION = 81;
private Context mContext;
@@ -1073,9 +1072,55 @@
upgradeVersion = 79;
}
+ if (upgradeVersion == 79) {
+ // Before touch exploration was a global setting controlled by the user
+ // via the UI. However, if the enabled accessibility services do not
+ // handle touch exploration mode, enabling it makes no sense. Therefore,
+ // now the services request touch exploration mode and the user is
+ // presented with a dialog to allow that and if she does we store that
+ // in the database. As a result of this change a user that has enabled
+ // accessibility, touch exploration, and some accessibility services
+ // may lose touch exploration state, thus rendering the device useless
+ // unless sighted help is provided, since the enabled service(s) are
+ // not in the list of services to which the user granted a permission
+ // to put the device in touch explore mode. Here we are allowing all
+ // enabled accessibility services to toggle touch exploration provided
+ // accessibility and touch exploration are enabled and no services can
+ // toggle touch exploration. Note that the user has already manually
+ // enabled the services and touch exploration which means the she has
+ // given consent to have these services work in touch exploration mode.
+ final boolean accessibilityEnabled = getIntValueFromTable(db, "secure",
+ Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
+ final boolean touchExplorationEnabled = getIntValueFromTable(db, "secure",
+ Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0) == 1;
+ if (accessibilityEnabled && touchExplorationEnabled) {
+ String enabledServices = getStringValueFromTable(db, "secure",
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "");
+ String touchExplorationGrantedServices = getStringValueFromTable(db, "secure",
+ Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, "");
+ if (TextUtils.isEmpty(touchExplorationGrantedServices)
+ && !TextUtils.isEmpty(enabledServices)) {
+ SQLiteStatement stmt = null;
+ try {
+ db.beginTransaction();
+ stmt = db.compileStatement("INSERT OR REPLACE INTO secure(name,value)"
+ + " VALUES(?,?);");
+ loadSetting(stmt,
+ Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
+ enabledServices);
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ if (stmt != null) stmt.close();
+ }
+ }
+ }
+ upgradeVersion = 80;
+ }
+
// vvv Jelly Bean MR1 changes begin here vvv
- if (upgradeVersion == 79) {
+ if (upgradeVersion == 80) {
// update screensaver settings
db.beginTransaction();
SQLiteStatement stmt = null;
@@ -1093,10 +1138,9 @@
db.endTransaction();
if (stmt != null) stmt.close();
}
- upgradeVersion = 80;
+ upgradeVersion = 81;
}
-
// *** Remember to update DATABASE_VERSION above!
if (upgradeVersion != currentVersion) {
@@ -1743,18 +1787,28 @@
}
private int getIntValueFromSystem(SQLiteDatabase db, String name, int defaultValue) {
- int value = defaultValue;
+ return getIntValueFromTable(db, "system", name, defaultValue);
+ }
+
+ private int getIntValueFromTable(SQLiteDatabase db, String table, String name,
+ int defaultValue) {
+ String value = getStringValueFromTable(db, table, name, null);
+ return (value != null) ? Integer.parseInt(value) : defaultValue;
+ }
+
+ private String getStringValueFromTable(SQLiteDatabase db, String table, String name,
+ String defaultValue) {
Cursor c = null;
try {
- c = db.query("system", new String[] { Settings.System.VALUE }, "name='" + name + "'",
+ c = db.query(table, new String[] { Settings.System.VALUE }, "name='" + name + "'",
null, null, null, null);
if (c != null && c.moveToFirst()) {
String val = c.getString(0);
- value = val == null ? defaultValue : Integer.parseInt(val);
+ return val == null ? defaultValue : val;
}
} finally {
if (c != null) c.close();
}
- return value;
+ return defaultValue;
}
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 51dc3b1..e13378f 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -15,6 +15,8 @@
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
<uses-permission android:name="android.permission.REMOTE_AUDIO_PLAYBACK" />
+ <uses-permission android:name="android.permission.MANAGE_USERS" />
+
<!-- Networking and telephony -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
diff --git a/packages/SystemUI/res/layout/quick_settings.xml b/packages/SystemUI/res/layout/quick_settings.xml
index a62a470..c4b881e 100644
--- a/packages/SystemUI/res/layout/quick_settings.xml
+++ b/packages/SystemUI/res/layout/quick_settings.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
-<com.android.systemui.statusbar.phone.PanelView
+<com.android.systemui.statusbar.phone.SettingsPanelView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -42,4 +42,4 @@
android:src="@drawable/status_bar_close"
/>
</LinearLayout>
-</com.android.systemui.statusbar.phone.PanelView>
\ No newline at end of file
+</com.android.systemui.statusbar.phone.SettingsPanelView >
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 828dba4..cb32d63 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -23,10 +23,11 @@
xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
android:id="@+id/notification_panel"
android:layout_width="0dp"
- android:layout_height="0dp"
+ android:layout_height="wrap_content"
android:background="@drawable/notification_panel_bg"
android:paddingTop="@dimen/notification_panel_padding_top"
android:layout_marginLeft="@dimen/notification_panel_margin_left"
+ android:animateLayoutChanges="true"
>
<TextView
@@ -45,6 +46,7 @@
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/close_handle_underlap"
android:orientation="vertical"
+ android:animateLayoutChanges="true"
>
<include layout="@layout/status_bar_expanded_header"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
index 7f598b6..60896c3 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
@@ -65,11 +65,24 @@
android:layout_weight="1"
/>
+ <TextView
+ android:id="@+id/header_debug_info"
+ android:visibility="invisible"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:fontFamily="sans-serif-condensed"
+ android:textSize="11dp"
+ android:textStyle="bold"
+ android:textColor="#00A040"
+ android:padding="2dp"
+ />
+
<ImageView android:id="@+id/clear_all_button"
android:layout_width="48dp"
android:layout_height="48dp"
android:scaleType="center"
android:src="@drawable/ic_notify_clear"
android:contentDescription="@string/accessibility_clear_all"
- />
-</LinearLayout>
\ No newline at end of file
+ />
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index dd70166..3dcdae8 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -49,4 +49,17 @@
android:background="@drawable/bottom_divider_glow"
/>
+ <TextView
+ android:id="@+id/debug_info"
+ android:visibility="invisible"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|right"
+ android:fontFamily="sans-serif-condensed"
+ android:textSize="9dp"
+ android:textStyle="bold"
+ android:textColor="#00A040"
+ android:padding="2dp"
+ />
+
</FrameLayout>
diff --git a/packages/SystemUI/src/com/android/systemui/SearchPanelView.java b/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
index b36e71a..281f25f 100644
--- a/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
@@ -24,7 +24,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
-import android.os.UserId;
+import android.os.UserHandle;
import android.os.Vibrator;
import android.provider.Settings;
import android.util.AttributeSet;
@@ -75,14 +75,15 @@
mBar.animateCollapse(CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL);
// Launch Assist
Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
- .getAssistIntent(mContext, UserId.USER_CURRENT);
+ .getAssistIntent(mContext, UserHandle.USER_CURRENT);
if (intent == null) return;
try {
ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
R.anim.search_launch_enter, R.anim.search_launch_exit,
getHandler(), this);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivityAsUser(intent, opts.toBundle(), UserId.USER_CURRENT);
+ mContext.startActivityAsUser(intent, opts.toBundle(),
+ new UserHandle(UserHandle.USER_CURRENT));
} catch (ActivityNotFoundException e) {
Slog.w(TAG, "Activity not found for " + intent.getAction());
onAnimationStarted();
@@ -143,7 +144,7 @@
private void maybeSwapSearchIcon() {
Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
- .getAssistIntent(mContext, UserId.USER_CURRENT);
+ .getAssistIntent(mContext, UserHandle.USER_CURRENT);
if (intent != null) {
ComponentName component = intent.getComponent();
if (component == null || !mGlowPadView.replaceTargetDrawablesIfPresent(component,
@@ -281,6 +282,6 @@
public boolean isAssistantAvailable() {
return ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
- .getAssistIntent(mContext, UserId.USER_CURRENT) != null;
+ .getAssistIntent(mContext, UserHandle.USER_CURRENT) != null;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
index 3e03f85..4d8c168 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
@@ -30,7 +30,7 @@
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Process;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.Log;
import com.android.systemui.R;
@@ -245,7 +245,7 @@
final List<ActivityManager.RecentTaskInfo> recentTasks =
am.getRecentTasksForUser(MAX_TASKS,
- ActivityManager.RECENT_IGNORE_UNAVAILABLE, UserId.USER_CURRENT);
+ ActivityManager.RECENT_IGNORE_UNAVAILABLE, UserHandle.USER_CURRENT);
int numTasks = recentTasks.size();
ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME).resolveActivityInfo(pm, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index 02e8937..d5a76bc 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -36,7 +36,7 @@
import android.net.Uri;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.UserId;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
@@ -790,7 +790,8 @@
| Intent.FLAG_ACTIVITY_TASK_ON_HOME
| Intent.FLAG_ACTIVITY_NEW_TASK);
if (DEBUG) Log.v(TAG, "Starting activity " + intent);
- context.startActivityAsUser(intent, opts.toBundle(), UserId.USER_CURRENT);
+ context.startActivityAsUser(intent, opts.toBundle(),
+ new UserHandle(UserHandle.USER_CURRENT));
}
if (usingDrawingCache) {
holder.thumbnailViewImage.setDrawingCacheEnabled(false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index ea5089d..646f98a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -34,8 +34,10 @@
import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.app.TaskStackBuilder;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.database.ContentObserver;
@@ -47,6 +49,7 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
@@ -65,6 +68,7 @@
import android.widget.LinearLayout;
import android.widget.PopupMenu;
import android.widget.RemoteViews;
+import android.widget.TextView;
import java.util.ArrayList;
@@ -72,6 +76,7 @@
CommandQueue.Callbacks, RecentsPanelView.OnRecentsPanelVisibilityChangedListener {
static final String TAG = "StatusBar";
private static final boolean DEBUG = false;
+ public static final boolean MULTIUSER_DEBUG = false;
protected static final int MSG_OPEN_RECENTS_PANEL = 1020;
protected static final int MSG_CLOSE_RECENTS_PANEL = 1021;
@@ -112,6 +117,8 @@
protected PopupMenu mNotificationBlamePopup;
+ protected int mCurrentUserId = 0;
+
// UI-specific methods
/**
@@ -252,6 +259,40 @@
switches[3]
));
}
+
+ // XXX: this is currently broken and will always return 0, but should start working at some point
+ try {
+ mCurrentUserId = ActivityManagerNative.getDefault().getCurrentUser().id;
+ } catch (RemoteException e) {
+ Log.v(TAG, "Couldn't get current user ID; guessing it's 0", e);
+ }
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_USER_SWITCHED);
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+ mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ if (true) Slog.v(TAG, "userId " + mCurrentUserId + " is in the house");
+ userSwitched(mCurrentUserId);
+ }
+ }}, filter);
+ }
+
+ public void userSwitched(int newUserId) {
+ // should be overridden
+ }
+
+ public boolean notificationIsForCurrentUser(StatusBarNotification n) {
+ final int thisUserId = mCurrentUserId;
+ final int notificationUserId = n.getUserId();
+ if (DEBUG && MULTIUSER_DEBUG) {
+ Slog.v(TAG, String.format("%s: current userid: %d, notification userid: %d",
+ n, thisUserId, notificationUserId));
+ }
+ return thisUserId == notificationUserId;
}
protected View updateNotificationVetoButton(View row, StatusBarNotification n) {
@@ -604,6 +645,14 @@
applyLegacyRowBackground(sbn, content);
row.setTag(R.id.expandable_tag, Boolean.valueOf(large != null));
+
+ if (MULTIUSER_DEBUG) {
+ TextView debug = (TextView) row.findViewById(R.id.debug_info);
+ if (debug != null) {
+ debug.setVisibility(View.VISIBLE);
+ debug.setText("U " + entry.notification.getUserId());
+ }
+ }
entry.row = row;
entry.content = content;
entry.expanded = expandedOneU;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java b/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java
index 81a16ae..0f894a1 100755
--- a/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java
@@ -101,8 +101,7 @@
mDownTime = ev.getDownTime();
} else {
if (mDownTime != ev.getDownTime()) {
- // TODO: remove
- throw new RuntimeException("Assertion failure in GestureRecorder: event downTime ("
+ Slog.w(TAG, "Assertion failure in GestureRecorder: event downTime ("
+ev.getDownTime()+") does not match gesture downTime ("+mDownTime+")");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index ae7fb27..13a34ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -22,15 +22,13 @@
public class NotificationPanelView extends PanelView {
public NotificationPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
- android.util.Slog.v("NotificationPanelView", "ctor");
}
-
@Override
public void fling(float vel, boolean always) {
((PhoneStatusBarView) mBar).mBar.getGestureRecorder().tag(
"fling " + ((vel > 0) ? "open" : "closed"),
- "v=" + vel);
+ "notifications,v=" + vel);
super.fling(vel, always);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index f44c500..bffb903 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -64,15 +64,25 @@
return getMeasuredHeight();
}
+ public PanelView selectPanelForTouchX(float x) {
+ final int N = mPanels.size();
+ return mPanels.get((int)(N * x / getMeasuredWidth()));
+ }
+
+ public boolean panelsEnabled() {
+ return true;
+ }
+
@Override
public boolean onTouchEvent(MotionEvent event) {
+ // Allow subclasses to implement enable/disable semantics
+ if (!panelsEnabled()) return false;
+
// figure out which panel needs to be talked to here
if (event.getAction() == MotionEvent.ACTION_DOWN) {
- final int N = mPanels.size();
- final int i = (int)(N * event.getX() / getMeasuredWidth());
- mTouchingPanel = mPanels.get(i);
+ mTouchingPanel = selectPanelForTouchX(event.getX());
mPanelHolder.setSelectedPanel(mTouchingPanel);
- LOG("PanelBar.onTouch: state=%d ACTION_DOWN: panel %d", mState, i);
+ LOG("PanelBar.onTouch: state=%d ACTION_DOWN: panel %s", mState, mTouchingPanel.getName());
if (mState == STATE_CLOSED || mState == STATE_OPEN) {
go(STATE_TRANSITIONING);
onPanelPeeked();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index b5a50c6..b595257 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -22,6 +22,7 @@
}
public static final boolean BRAKES = false;
+ private static final boolean STRETCH_PAST_CONTENTS = true;
private float mSelfExpandVelocityPx; // classic value: 2000px/s
private float mSelfCollapseVelocityPx; // classic value: 2000px/s (will be negated to collapse "up")
@@ -45,6 +46,9 @@
private float mTouchOffset;
private float mExpandedFraction = 0;
private float mExpandedHeight = 0;
+ private boolean mClosing;
+ private boolean mRubberbanding;
+ private boolean mTracking;
private TimeAnimator mTimeAnimator;
private VelocityTracker mVelocityTracker;
@@ -59,9 +63,15 @@
}
};
+ private final Runnable mStopAnimator = new Runnable() { public void run() {
+ if (mTimeAnimator.isStarted()) {
+ mTimeAnimator.end();
+ }
+ }};
+
private float mVel, mAccel;
private int mFullHeight = 0;
- private String mViewName;
+ private String mViewName;
private void animationTick(long dtms) {
if (!mTimeAnimator.isStarted()) {
@@ -70,16 +80,18 @@
mTimeAnimator.setTimeListener(mAnimationCallback);
mTimeAnimator.start();
+
+ mRubberbanding = STRETCH_PAST_CONTENTS && mExpandedHeight > getFullHeight();
+ mClosing = (mExpandedHeight > 0 && mVel < 0) || mRubberbanding;
} else if (dtms > 0) {
final float dt = dtms * 0.001f; // ms -> s
LOG("tick: v=%.2fpx/s dt=%.4fs", mVel, dt);
LOG("tick: before: h=%d", (int) mExpandedHeight);
final float fh = getFullHeight();
- final boolean closing = mExpandedHeight > 0 && mVel < 0;
boolean braking = false;
if (BRAKES) {
- if (closing) {
+ if (mClosing) {
braking = mExpandedHeight <= mCollapseBrakingDistancePx;
mAccel = braking ? 10*mCollapseAccelPx : -mCollapseAccelPx;
} else {
@@ -87,37 +99,41 @@
mAccel = braking ? 10*-mExpandAccelPx : mExpandAccelPx;
}
} else {
- mAccel = closing ? -mCollapseAccelPx : mExpandAccelPx;
+ mAccel = mClosing ? -mCollapseAccelPx : mExpandAccelPx;
}
mVel += mAccel * dt;
if (braking) {
- if (closing && mVel > -mBrakingSpeedPx) {
+ if (mClosing && mVel > -mBrakingSpeedPx) {
mVel = -mBrakingSpeedPx;
- } else if (!closing && mVel < mBrakingSpeedPx) {
+ } else if (!mClosing && mVel < mBrakingSpeedPx) {
mVel = mBrakingSpeedPx;
}
} else {
- if (closing && mVel > -mFlingCollapseMinVelocityPx) {
+ if (mClosing && mVel > -mFlingCollapseMinVelocityPx) {
mVel = -mFlingCollapseMinVelocityPx;
- } else if (!closing && mVel > mFlingGestureMaxOutputVelocityPx) {
+ } else if (!mClosing && mVel > mFlingGestureMaxOutputVelocityPx) {
mVel = mFlingGestureMaxOutputVelocityPx;
}
}
float h = mExpandedHeight + mVel * dt;
+
+ if (mRubberbanding && h < fh) {
+ h = fh;
+ }
- LOG("tick: new h=%d closing=%s", (int) h, closing?"true":"false");
+ LOG("tick: new h=%d closing=%s", (int) h, mClosing?"true":"false");
setExpandedHeightInternal(h);
mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);
if (mVel == 0
- || (closing && mExpandedHeight == 0)
- || (!closing && mExpandedHeight == getFullHeight())) {
- mTimeAnimator.end();
+ || (mClosing && mExpandedHeight == 0)
+ || ((mRubberbanding || !mClosing) && mExpandedHeight == fh)) {
+ post(mStopAnimator);
}
}
}
@@ -178,6 +194,7 @@
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
+ mTracking = true;
mVelocityTracker = VelocityTracker.obtain();
trackMovement(event);
mBar.onTrackingStarted(PanelView.this);
@@ -185,7 +202,7 @@
break;
case MotionEvent.ACTION_MOVE:
- PanelView.this.setExpandedHeight(rawY - mAbsPos[1] - mTouchOffset);
+ PanelView.this.setExpandedHeightInternal(rawY - mAbsPos[1] - mTouchOffset);
mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);
@@ -194,6 +211,7 @@
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
+ mTracking = false;
mBar.onTrackingStopped(PanelView.this);
trackMovement(event);
mVelocityTracker.computeCurrentVelocity(1000);
@@ -223,7 +241,7 @@
xVel, yVel,
vel);
- fling(vel, false);
+ fling(vel, true);
mVelocityTracker.recycle();
mVelocityTracker = null;
@@ -238,7 +256,7 @@
public void fling(float vel, boolean always) {
mVel = vel;
- if (mVel != 0) {
+ if (always||mVel != 0) {
animationTick(0); // begin the animation
}
}
@@ -270,6 +288,10 @@
LOG("onMeasure(%d, %d) -> (%d, %d)",
widthMeasureSpec, heightMeasureSpec, getMeasuredWidth(), getMeasuredHeight());
mFullHeight = getMeasuredHeight();
+ // if one of our children is getting smaller, we should track that
+ if (!mTracking && !mRubberbanding && !mTimeAnimator.isStarted() && mExpandedHeight > 0 && mExpandedHeight != mFullHeight) {
+ mExpandedHeight = mFullHeight;
+ }
heightMeasureSpec = MeasureSpec.makeMeasureSpec(
(int) mExpandedHeight, MeasureSpec.AT_MOST); // MeasureSpec.getMode(heightMeasureSpec));
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
@@ -277,21 +299,26 @@
public void setExpandedHeight(float height) {
- mTimeAnimator.end();
+ post(mStopAnimator);
setExpandedHeightInternal(height);
}
+ @Override
+ protected void onLayout (boolean changed, int left, int top, int right, int bottom) {
+ LOG("onLayout: changed=%s, bottom=%d eh=%d fh=%d", changed?"T":"f", bottom, (int)mExpandedHeight, (int)mFullHeight);
+ super.onLayout(changed, left, top, right, bottom);
+ }
+
public void setExpandedHeightInternal(float h) {
float fh = getFullHeight();
if (fh == 0) {
// Hmm, full height hasn't been computed yet
}
- LOG("setExpansion: height=%.1f fh=%.1f", h, fh);
+ LOG("setExpansion: height=%.1f fh=%.1f tracking=%s rubber=%s", h, fh, mTracking?"T":"f", mRubberbanding?"T":"f");
if (h < 0) h = 0;
- else if (h > fh) h = fh;
-
+ if (!(STRETCH_PAST_CONTENTS && (mTracking || mRubberbanding)) && h > fh) h = fh;
mExpandedHeight = h;
requestLayout();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 4d4adcb8..49e4760 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -46,7 +46,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
import android.provider.Settings;
import android.service.dreams.IDreamManager;
import android.util.DisplayMetrics;
@@ -109,7 +109,7 @@
public static final String ACTION_STATUSBAR_START
= "com.android.internal.policy.statusbar.START";
- private static final boolean SHOW_CARRIER_LABEL = true;
+ private static final boolean SHOW_CARRIER_LABEL = false; // XXX: doesn't work with rubberband panels right now
private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
private static final int MSG_CLOSE_NOTIFICATION_PANEL = 1001;
@@ -174,6 +174,7 @@
int mNotificationPanelMarginBottomPx, mNotificationPanelMarginPx;
int mNotificationPanelMinHeight;
boolean mNotificationPanelIsFullScreenWidth;
+ TextView mNotificationPanelDebugText;
// settings
PanelView mSettingsPanel;
@@ -343,6 +344,10 @@
mIntruderAlertView.setVisibility(View.GONE);
mIntruderAlertView.setBar(this);
}
+ if (MULTIUSER_DEBUG) {
+ mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(R.id.header_debug_info);
+ mNotificationPanelDebugText.setVisibility(View.VISIBLE);
+ }
updateShowSearchHoldoff();
@@ -806,9 +811,9 @@
// If the device hasn't been through Setup, we only show system notifications
for (int i=0; i<N; i++) {
Entry ent = mNotificationData.get(N-i-1);
- if (provisioned || showNotificationEvenIfUnprovisioned(ent.notification)) {
- toShow.add(ent.row);
- }
+ if (!(provisioned || showNotificationEvenIfUnprovisioned(ent.notification))) continue;
+ if (!notificationIsForCurrentUser(ent.notification)) continue;
+ toShow.add(ent.row);
}
ArrayList<View> toRemove = new ArrayList<View>();
@@ -854,10 +859,10 @@
// If the device hasn't been through Setup, we only show system notifications
for (int i=0; i<N; i++) {
Entry ent = mNotificationData.get(N-i-1);
- if ((provisioned && ent.notification.score >= HIDE_ICONS_BELOW_SCORE)
- || showNotificationEvenIfUnprovisioned(ent.notification)) {
- toShow.add(ent.icon);
- }
+ if (!((provisioned && ent.notification.score >= HIDE_ICONS_BELOW_SCORE)
+ || showNotificationEvenIfUnprovisioned(ent.notification))) continue;
+ if (!notificationIsForCurrentUser(ent.notification)) continue;
+ toShow.add(ent.icon);
}
ArrayList<View> toRemove = new ArrayList<View>();
@@ -1304,10 +1309,6 @@
mGestureRec.add(event);
- if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
- return false;
- }
-
return false;
}
@@ -1782,13 +1783,15 @@
} catch (RemoteException e) {
}
v.getContext().startActivityAsUser(new Intent(Settings.ACTION_SETTINGS)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK), UserId.USER_CURRENT);
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
+ new UserHandle(UserHandle.USER_CURRENT));
animateCollapse();
}
};
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
+ Slog.v(TAG, "onReceive: " + intent);
String action = intent.getAction();
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
int flags = CommandQueue.FLAG_EXCLUDE_NONE;
@@ -1812,6 +1815,13 @@
}
};
+ @Override
+ public void userSwitched(int newUserId) {
+ if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
+ animateCollapse();
+ updateNotificationIcons();
+ }
+
private void setIntruderAlertVisibility(boolean vis) {
if (!ENABLE_INTRUDERS) return;
if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 25ff9aa..2a96d6d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone;
import android.app.ActivityManager;
+import android.app.StatusBarManager;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -43,6 +44,7 @@
PhoneStatusBar mBar;
int mScrimColor;
PanelView mFadingPanel = null;
+ PanelView mNotificationPanel, mSettingsPanel;
public PhoneStatusBarView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -59,6 +61,21 @@
}
@Override
+ public void addPanel(PanelView pv) {
+ super.addPanel(pv);
+ if (pv.getId() == R.id.notification_panel) {
+ mNotificationPanel = pv;
+ } else if (pv.getId() == R.id.settings_panel){
+ mSettingsPanel = pv;
+ }
+ }
+
+ @Override
+ public boolean panelsEnabled() {
+ return ((mBar.mDisabled & StatusBarManager.DISABLE_EXPAND) == 0);
+ }
+
+ @Override
public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
if (super.onRequestSendAccessibilityEvent(child, event)) {
// The status bar is very small so augment the view that the user is touching
@@ -74,6 +91,20 @@
}
@Override
+ public PanelView selectPanelForTouchX(float x) {
+ // We split the status bar into thirds: the left 2/3 are for notifications, and the
+ // right 1/3 for quick settings. If you pull the status bar down a second time you'll
+ // toggle panels no matter where you pull it down.
+ final float w = (float) getMeasuredWidth();
+ final float f = x / w;
+ if (f > 0.67f && mSettingsPanel.getExpandedFraction() != 1.0f
+ || mNotificationPanel.getExpandedFraction() == 1.0f) {
+ return mSettingsPanel;
+ }
+ return mNotificationPanel;
+ }
+
+ @Override
public void onPanelPeeked() {
super.onPanelPeeked();
mBar.makeExpandedVisible(true);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsPanelView.java
new file mode 100644
index 0000000..fb1528f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsPanelView.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+public class SettingsPanelView extends PanelView {
+ public SettingsPanelView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public void fling(float vel, boolean always) {
+ ((PhoneStatusBarView) mBar).mBar.getGestureRecorder().tag(
+ "fling " + ((vel > 0) ? "open" : "closed"),
+ "settings,v=" + vel);
+ super.fling(vel, always);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
index e63735677..89eed1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
@@ -78,6 +78,7 @@
super(context, attrs, defStyle);
mRealLayoutTransition = new LayoutTransition();
+ mRealLayoutTransition.setAnimateParentHierarchy(true);
setLayoutTransitionsEnabled(true);
setOrientation(LinearLayout.VERTICAL);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java
index da161a9..c45ac3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java
@@ -19,7 +19,7 @@
import android.app.StatusBarManager;
import android.content.Context;
import android.content.Intent;
-import android.os.UserId;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Slog;
@@ -119,7 +119,8 @@
// ----------------------------
private void onClickSettings() {
getContext().startActivityAsUser(new Intent(Settings.ACTION_SETTINGS)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK), UserId.USER_CURRENT);
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
+ new UserHandle(UserHandle.USER_CURRENT));
getStatusBarManager().collapse();
}
}
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
index a939222..279314d 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
@@ -178,10 +178,10 @@
mHandler.sendMessage(mHandler.obtainMessage(MSG_DPM_STATE_CHANGED));
} else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHED,
- intent.getIntExtra(Intent.EXTRA_USERID, 0), 0));
+ intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0));
} else if (Intent.ACTION_USER_REMOVED.equals(action)) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_REMOVED,
- intent.getIntExtra(Intent.EXTRA_USERID, 0), 0));
+ intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0));
}
}
};
diff --git a/policy/src/com/android/internal/policy/impl/LockScreen.java b/policy/src/com/android/internal/policy/impl/LockScreen.java
index 8645172..91b5ca1 100644
--- a/policy/src/com/android/internal/policy/impl/LockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/LockScreen.java
@@ -32,7 +32,7 @@
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.os.UserId;
+import android.os.UserHandle;
import android.os.Vibrator;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -277,7 +277,7 @@
// Update the search icon with drawable from the search .apk
if (!mSearchDisabled) {
Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
- .getAssistIntent(mContext, UserId.USER_CURRENT);
+ .getAssistIntent(mContext, UserHandle.USER_CURRENT);
if (intent != null) {
// XXX Hack. We need to substitute the icon here but haven't formalized
// the public API. The "_google" metadata will be going away, so
@@ -313,7 +313,7 @@
case com.android.internal.R.drawable.ic_action_assist_generic:
Intent assistIntent =
((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
- .getAssistIntent(mContext, UserId.USER_CURRENT);
+ .getAssistIntent(mContext, UserHandle.USER_CURRENT);
if (assistIntent != null) {
launchActivity(assistIntent);
} else {
@@ -354,7 +354,7 @@
Log.w(TAG, "can't dismiss keyguard on launch");
}
try {
- mContext.startActivityAsUser(intent, UserId.USER_CURRENT);
+ mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
} catch (ActivityNotFoundException e) {
Log.w(TAG, "Activity not found for intent + " + intent.getAction());
}
@@ -532,7 +532,7 @@
}
boolean searchActionAvailable =
((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
- .getAssistIntent(mContext, UserId.USER_CURRENT) != null;
+ .getAssistIntent(mContext, UserHandle.USER_CURRENT) != null;
mCameraDisabled = disabledByAdmin || disabledBySimState || !cameraTargetPresent;
mSearchDisabled = disabledBySimState || !searchActionAvailable || !searchTargetPresent;
mUnlockWidgetMethods.updateResources();
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index c036e1b..737df7f 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -54,7 +54,7 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UEventObserver;
-import android.os.UserId;
+import android.os.UserHandle;
import android.os.Vibrator;
import android.provider.Settings;
@@ -1717,15 +1717,17 @@
mHomePressed = false;
mHomeLongPressed = false;
if (!homeWasLongPressed) {
- try {
- IStatusBarService statusbar = getStatusBarService();
- if (statusbar != null) {
- statusbar.cancelPreloadRecentApps();
+ if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_SYSTEM_UI) {
+ try {
+ IStatusBarService statusbar = getStatusBarService();
+ if (statusbar != null) {
+ statusbar.cancelPreloadRecentApps();
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException when showing recent apps", e);
+ // re-acquire status bar service next time it is needed.
+ mStatusBarService = null;
}
- } catch (RemoteException e) {
- Slog.e(TAG, "RemoteException when showing recent apps", e);
- // re-acquire status bar service next time it is needed.
- mStatusBarService = null;
}
mHomePressed = false;
@@ -2064,7 +2066,7 @@
if (searchManager != null) {
searchManager.stopSearch();
}
- mContext.startActivityAsUser(intent, UserId.USER_CURRENT);
+ mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
} catch (ActivityNotFoundException e) {
Slog.w(TAG, "No activity to handle assist long press action.", e);
}
@@ -2073,13 +2075,13 @@
private void launchAssistAction() {
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_ASSIST);
Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
- .getAssistIntent(mContext, UserId.USER_CURRENT);
+ .getAssistIntent(mContext, UserHandle.USER_CURRENT);
if (intent != null) {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_SINGLE_TOP
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
try {
- mContext.startActivityAsUser(intent, UserId.USER_CURRENT);
+ mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
} catch (ActivityNotFoundException e) {
Slog.w(TAG, "No activity to handle assist action.", e);
}
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 38f4554..8e341df 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -177,7 +177,7 @@
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- onUserRemoved(intent.getIntExtra(Intent.EXTRA_USERID, -1));
+ onUserRemoved(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
}
}, userFilter);
}
diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java
index 48f967c..539e561 100644
--- a/services/java/com/android/server/AppWidgetServiceImpl.java
+++ b/services/java/com/android/server/AppWidgetServiceImpl.java
@@ -43,7 +43,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.AtomicFile;
import android.util.AttributeSet;
import android.util.Log;
@@ -499,7 +499,7 @@
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
intent.setComponent(p.info.provider);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
- mContext.sendBroadcastToUser(intent, mUserId);
+ mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
if (p.instances.size() == 0) {
// cancel the future updates
cancelBroadcasts(p);
@@ -507,7 +507,7 @@
// 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.sendBroadcastToUser(intent, mUserId);
+ mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
}
}
}
@@ -593,7 +593,7 @@
private boolean callerHasBindAppWidgetPermission(String packageName) {
int callingUid = Binder.getCallingUid();
try {
- if (!UserId.isSameApp(callingUid, getUidForPackage(packageName))) {
+ if (!UserHandle.isSameApp(callingUid, getUidForPackage(packageName))) {
return false;
}
} catch (Exception e) {
@@ -665,7 +665,7 @@
mBoundRemoteViewsServices.remove(key);
}
- int userId = UserId.getUserId(id.provider.uid);
+ int userId = UserHandle.getUserId(id.provider.uid);
// Bind to the RemoteViewsService (which will trigger a callback to the
// RemoteViewsAdapter.onServiceConnected())
final long token = Binder.clearCallingIdentity();
@@ -756,7 +756,7 @@
}
};
- int userId = UserId.getUserId(id.provider.uid);
+ int userId = UserHandle.getUserId(id.provider.uid);
// Bind to the service and remove the static intent->factory mapping in the
// RemoteViewsService.
final long token = Binder.clearCallingIdentity();
@@ -880,7 +880,7 @@
intent.setComponent(p.info.provider);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
- mContext.sendBroadcastToUser(intent, mUserId);
+ mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
}
}
@@ -1026,7 +1026,7 @@
}
};
- int userId = UserId.getUserId(id.provider.uid);
+ int userId = UserHandle.getUserId(id.provider.uid);
// Bind to the service and call onDataSetChanged()
final long token = Binder.clearCallingIdentity();
try {
@@ -1205,7 +1205,7 @@
void sendEnableIntentLocked(Provider p) {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
intent.setComponent(p.info.provider);
- mContext.sendBroadcastToUser(intent, mUserId);
+ mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
}
void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
@@ -1213,7 +1213,7 @@
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
intent.setComponent(p.info.provider);
- mContext.sendBroadcastToUser(intent, mUserId);
+ mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
}
}
@@ -1375,7 +1375,7 @@
throw new IllegalArgumentException("packageName and uid don't match packageName="
+ packageName);
}
- if (!UserId.isSameApp(callingUid, packageUid)) {
+ if (!UserHandle.isSameApp(callingUid, packageUid)) {
throw new IllegalArgumentException("packageName and uid don't match packageName="
+ packageName);
}
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 4542840..8be0ba8 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -65,7 +65,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
import android.os.WorkSource;
import android.os.storage.IMountService;
import android.provider.Settings;
@@ -4846,8 +4846,8 @@
// ----- IBackupManager binder interface -----
public void dataChanged(final String packageName) {
- final int callingUserHandle = UserId.getCallingUserId();
- if (callingUserHandle != UserId.USER_OWNER) {
+ final int callingUserHandle = UserHandle.getCallingUserId();
+ if (callingUserHandle != UserHandle.USER_OWNER) {
// App is running under a non-owner user profile. For now, we do not back
// up data from secondary user profiles.
// TODO: backups for all user profiles.
@@ -4950,8 +4950,8 @@
boolean doAllApps, boolean includeSystem, String[] pkgList) {
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
- final int callingUserHandle = UserId.getCallingUserId();
- if (callingUserHandle != UserId.USER_OWNER) {
+ final int callingUserHandle = UserHandle.getCallingUserId();
+ if (callingUserHandle != UserHandle.USER_OWNER) {
throw new IllegalStateException("Backup supported only for the device owner");
}
@@ -5019,8 +5019,8 @@
public void fullRestore(ParcelFileDescriptor fd) {
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullRestore");
- final int callingUserHandle = UserId.getCallingUserId();
- if (callingUserHandle != UserId.USER_OWNER) {
+ final int callingUserHandle = UserHandle.getCallingUserId();
+ if (callingUserHandle != UserHandle.USER_OWNER) {
throw new IllegalStateException("Restore supported only for the device owner");
}
diff --git a/services/java/com/android/server/ClipboardService.java b/services/java/com/android/server/ClipboardService.java
index 8a6a550..0bf7aad 100644
--- a/services/java/com/android/server/ClipboardService.java
+++ b/services/java/com/android/server/ClipboardService.java
@@ -36,7 +36,7 @@
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -96,7 +96,7 @@
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_USER_REMOVED.equals(action)) {
- removeClipboard(intent.getIntExtra(Intent.EXTRA_USERID, 0));
+ removeClipboard(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
}
}
}, userFilter);
@@ -115,7 +115,7 @@
}
private PerUserClipboard getClipboard() {
- return getClipboard(UserId.getCallingUserId());
+ return getClipboard(UserHandle.getCallingUserId());
}
private PerUserClipboard getClipboard(int userId) {
@@ -258,7 +258,7 @@
PackageInfo pi;
try {
pi = mPm.getPackageInfo(pkg, 0);
- if (!UserId.isSameApp(pi.applicationInfo.uid, uid)) {
+ if (!UserHandle.isSameApp(pi.applicationInfo.uid, uid)) {
throw new SecurityException("Calling uid " + uid
+ " does not own package " + pkg);
}
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index e219e8d..bb005d9 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -47,7 +47,6 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
-import android.os.Parcelable;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
@@ -64,6 +63,7 @@
import com.android.server.location.GeocoderProxy;
import com.android.server.location.GeofenceManager;
import com.android.server.location.GpsLocationProvider;
+import com.android.server.location.LocationFudger;
import com.android.server.location.LocationProviderInterface;
import com.android.server.location.LocationProviderProxy;
import com.android.server.location.MockProvider;
@@ -110,19 +110,6 @@
private static final int MSG_LOCATION_CHANGED = 1;
- // Accuracy in meters above which a location is considered coarse
- private static final double COARSE_ACCURACY_M = 100.0;
- private static final String EXTRA_COARSE_LOCATION = "coarseLocation";
-
- private static final int APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR = 111000;
-
- /**
- * Maximum latitude of 1 meter from the pole.
- * This keeps cosine(MAX_LATITUDE) to a non-zero value;
- */
- private static final double MAX_LATITUDE =
- 90.0 - (1.0 / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR);
-
// Location Providers may sometimes deliver location updates
// slightly faster that requested - provide grace period so
// we don't unnecessarily filter events that are otherwise on
@@ -137,6 +124,7 @@
private final Object mLock = new Object();
// --- fields below are final after init() ---
+ private LocationFudger mLocationFudger;
private GeofenceManager mGeofenceManager;
private PowerManager.WakeLock mWakeLock;
private PackageManager mPackageManager;
@@ -221,6 +209,7 @@
loadProvidersLocked();
}
mGeofenceManager = new GeofenceManager(mContext);
+ mLocationFudger = new LocationFudger();
// Register for Network (Wifi or Mobile) updates
IntentFilter filter = new IntentFilter();
@@ -907,7 +896,25 @@
String perm = checkPermission();
if (ACCESS_COARSE_LOCATION.equals(perm)) {
- request.applyCoarsePermissionRestrictions();
+ switch (request.getQuality()) {
+ case LocationRequest.ACCURACY_FINE:
+ request.setQuality(LocationRequest.ACCURACY_BLOCK);
+ break;
+ case LocationRequest.POWER_HIGH:
+ request.setQuality(LocationRequest.POWER_LOW);
+ break;
+ }
+ // throttle
+ if (request.getInterval() < LocationFudger.FASTEST_INTERVAL_MS) {
+ request.setInterval(LocationFudger.FASTEST_INTERVAL_MS);
+ }
+ if (request.getFastestInterval() < LocationFudger.FASTEST_INTERVAL_MS) {
+ request.setFastestInterval(LocationFudger.FASTEST_INTERVAL_MS);
+ }
+ }
+ // make getFastestInterval() the minimum of interval and fastest interval
+ if (request.getFastestInterval() > request.getInterval()) {
+ request.setFastestInterval(request.getInterval());
}
return perm;
}
@@ -1075,7 +1082,7 @@
if (ACCESS_FINE_LOCATION.equals(perm)) {
return location;
} else {
- return getCoarseLocationExtra(location);
+ return mLocationFudger.getOrCreate(location);
}
}
}
@@ -1283,6 +1290,8 @@
}
private void handleLocationChangedLocked(Location location, boolean passive) {
+ if (D) Log.d(TAG, "incoming location: " + location);
+
long now = SystemClock.elapsedRealtime();
String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider());
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
@@ -1291,11 +1300,8 @@
LocationProviderInterface p = mProvidersByName.get(provider);
if (p == null) return;
- // Add the coarse location as an extra, if not already present
- Location coarse = getCoarseLocationExtra(location);
- if (coarse == null) {
- coarse = addCoarseLocationExtra(location);
- }
+ // Add the coarse location as an extra
+ Location coarse = mLocationFudger.getOrCreate(location);
// Update last known locations
Location lastLocation = mLastLocation.get(provider);
@@ -1660,106 +1666,6 @@
}
}
- private static double wrapLatitude(double lat) {
- if (lat > MAX_LATITUDE) lat = MAX_LATITUDE;
- if (lat < -MAX_LATITUDE) lat = -MAX_LATITUDE;
- return lat;
- }
-
- private static double wrapLongitude(double lon) {
- if (lon >= 180.0) lon -= 360.0;
- if (lon < -180.0) lon += 360.0;
- return lon;
- }
-
- private static double distanceToDegreesLatitude(double distance) {
- return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR;
- }
-
- /**
- * Requires latitude since longitudinal distances change with distance from equator.
- */
- private static double distanceToDegreesLongitude(double distance, double lat) {
- return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR / Math.cos(lat);
- }
-
- /**
- * Fudge a location into a coarse location.
- * <p>Add a random offset, then quantize the result (snap-to-grid).
- * Random offsets alone can be low-passed pretty easily.
- * Snap-to-grid on its own is excellent unless you are sitting on a
- * grid boundary and bouncing between quantizations.
- * The combination is quite hard to reverse engineer.
- * <p>The random offset used is smaller than the goal accuracy
- * ({@link #COARSE_ACCURACY_M}), in order to give relatively stable
- * results after quantization.
- */
- private static Location createCoarse(Location fine) {
- Location coarse = new Location(fine);
-
- coarse.removeBearing();
- coarse.removeSpeed();
- coarse.removeAltitude();
-
- double lat = coarse.getLatitude();
- double lon = coarse.getLongitude();
-
- // wrap
- lat = wrapLatitude(lat);
- lon = wrapLongitude(lon);
-
- if (coarse.getAccuracy() < COARSE_ACCURACY_M / 2) {
- // apply a random offset
- double fudgeDistance = COARSE_ACCURACY_M / 2.0 - coarse.getAccuracy();
- lat += (Math.random() - 0.5) * distanceToDegreesLatitude(fudgeDistance);
- lon += (Math.random() - 0.5) * distanceToDegreesLongitude(fudgeDistance, lat);
- }
-
- // wrap
- lat = wrapLatitude(lat);
- lon = wrapLongitude(lon);
-
- // quantize (snap-to-grid)
- double latGranularity = distanceToDegreesLatitude(COARSE_ACCURACY_M);
- double lonGranularity = distanceToDegreesLongitude(COARSE_ACCURACY_M, lat);
- long latQuantized = Math.round(lat / latGranularity);
- long lonQuantized = Math.round(lon / lonGranularity);
- lat = latQuantized * latGranularity;
- lon = lonQuantized * lonGranularity;
-
- // wrap again
- lat = wrapLatitude(lat);
- lon = wrapLongitude(lon);
-
- // apply
- coarse.setLatitude(lat);
- coarse.setLongitude(lon);
- coarse.setAccuracy((float)COARSE_ACCURACY_M);
-
- return coarse;
- }
-
-
- private static Location getCoarseLocationExtra(Location location) {
- Bundle extras = location.getExtras();
- if (extras == null) return null;
- Parcelable parcel = extras.getParcelable(EXTRA_COARSE_LOCATION);
- if (parcel == null) return null;
- if (!(parcel instanceof Location)) return null;
- Location coarse = (Location) parcel;
- if (coarse.getAccuracy() < COARSE_ACCURACY_M) return null;
- return coarse;
- }
-
- private static Location addCoarseLocationExtra(Location location) {
- Bundle extras = location.getExtras();
- if (extras == null) extras = new Bundle();
- Location coarse = createCoarse(location);
- extras.putParcelable(EXTRA_COARSE_LOCATION, coarse);
- location.setExtras(extras);
- return coarse;
- }
-
private void log(String log) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Slog.d(TAG, log);
@@ -1819,6 +1725,9 @@
}
}
+ pw.append(" fudger: ");
+ mLocationFudger.dump(fd, pw, args);
+
if (args.length > 0 && "short".equals(args[0])) {
return;
}
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 04267a3..bb5d552 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -48,7 +48,7 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.os.UserId;
+import android.os.UserHandle;
import android.os.storage.IMountService;
import android.os.storage.IMountServiceListener;
import android.os.storage.IMountShutdownObserver;
@@ -1713,7 +1713,7 @@
return false;
}
- final int packageUid = mPms.getPackageUid(packageName, UserId.getUserId(callerUid));
+ final int packageUid = mPms.getPackageUid(packageName, UserHandle.getUserId(callerUid));
if (DEBUG_OBB) {
Slog.d(TAG, "packageName = " + packageName + ", packageUid = " +
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 798915b..a565d08 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -48,7 +48,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.UserId;
+import android.os.UserHandle;
import android.os.Vibrator;
import android.provider.Settings;
import android.service.dreams.IDreamManager;
@@ -1286,7 +1286,7 @@
try {
ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo(
pkg, 0);
- if (!UserId.isSameApp(ai.uid, uid)) {
+ if (!UserHandle.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/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index d97d335..1d248b2 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -45,9 +45,10 @@
import android.os.FileObserver;
import android.os.ParcelFileDescriptor;
import android.os.RemoteCallbackList;
+import android.os.SELinux;
import android.os.ServiceManager;
import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
import android.service.wallpaper.IWallpaperConnection;
import android.service.wallpaper.IWallpaperEngine;
import android.service.wallpaper.IWallpaperService;
@@ -422,9 +423,9 @@
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
- switchUser(intent.getIntExtra(Intent.EXTRA_USERID, 0));
+ switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
} else if (Intent.ACTION_USER_REMOVED.equals(action)) {
- removeUser(intent.getIntExtra(Intent.EXTRA_USERID, 0));
+ removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
}
}
}, userFilter);
@@ -483,7 +484,7 @@
public void clearWallpaper() {
if (DEBUG) Slog.v(TAG, "clearWallpaper");
synchronized (mLock) {
- clearWallpaperLocked(false, UserId.getCallingUserId());
+ clearWallpaperLocked(false, UserHandle.getCallingUserId());
}
}
@@ -520,7 +521,7 @@
public void setDimensionHints(int width, int height) throws RemoteException {
checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
WallpaperData wallpaper = mWallpaperMap.get(userId);
if (wallpaper == null) {
throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
@@ -551,14 +552,14 @@
public int getWidthHint() throws RemoteException {
synchronized (mLock) {
- WallpaperData wallpaper = mWallpaperMap.get(UserId.getCallingUserId());
+ WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
return wallpaper.width;
}
}
public int getHeightHint() throws RemoteException {
synchronized (mLock) {
- WallpaperData wallpaper = mWallpaperMap.get(UserId.getCallingUserId());
+ WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
return wallpaper.height;
}
}
@@ -573,7 +574,7 @@
if (callingUid == android.os.Process.SYSTEM_UID) {
wallpaperUserId = mCurrentUserId;
} else {
- wallpaperUserId = UserId.getUserId(callingUid);
+ wallpaperUserId = UserHandle.getUserId(callingUid);
}
WallpaperData wallpaper = mWallpaperMap.get(wallpaperUserId);
try {
@@ -596,7 +597,7 @@
}
public WallpaperInfo getWallpaperInfo() {
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
synchronized (mLock) {
WallpaperData wallpaper = mWallpaperMap.get(userId);
if (wallpaper.connection != null) {
@@ -608,7 +609,7 @@
public ParcelFileDescriptor setWallpaper(String name) {
if (DEBUG) Slog.v(TAG, "setWallpaper");
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
WallpaperData wallpaper = mWallpaperMap.get(userId);
if (wallpaper == null) {
throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
@@ -639,8 +640,12 @@
FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
-1, -1);
}
- ParcelFileDescriptor fd = ParcelFileDescriptor.open(new File(dir, WALLPAPER),
+ File file = new File(dir, WALLPAPER);
+ ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
MODE_CREATE|MODE_READ_WRITE);
+ if (!SELinux.restorecon(file)) {
+ return null;
+ }
wallpaper.name = name;
return fd;
} catch (FileNotFoundException e) {
@@ -651,7 +656,7 @@
public void setWallpaperComponent(ComponentName name) {
if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name);
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
WallpaperData wallpaper = mWallpaperMap.get(userId);
if (wallpaper == null) {
throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index e1c05b5..edbc624 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -303,6 +303,10 @@
mWifiStateMachine.sendMessage(Message.obtain(msg));
break;
}
+ case WifiManager.RSSI_PKTCNT_FETCH: {
+ mWifiStateMachine.sendMessage(Message.obtain(msg));
+ break;
+ }
default: {
Slog.d(TAG, "WifiServicehandler.handleMessage ignoring msg=" + msg);
break;
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index 632c2f2..d8ccabb 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -51,7 +51,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
@@ -226,7 +226,7 @@
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType,
- callingPid, callingUid, UserId.getUserId(callingUid), true);
+ callingPid, callingUid, UserHandle.getUserId(callingUid), true);
if (res == null) {
return null;
}
@@ -279,7 +279,7 @@
// If this service is active, make sure it is stopped.
ServiceLookupResult r = retrieveServiceLocked(service, resolvedType,
Binder.getCallingPid(), Binder.getCallingUid(),
- callerApp == null ? UserId.getCallingUserId() : callerApp.userId,
+ callerApp == null ? UserHandle.getCallingUserId() : callerApp.userId,
false);
if (r != null) {
if (r.record != null) {
@@ -300,7 +300,7 @@
IBinder peekServiceLocked(Intent service, String resolvedType) {
ServiceLookupResult r = retrieveServiceLocked(service, resolvedType,
Binder.getCallingPid(), Binder.getCallingUid(),
- UserId.getCallingUserId(), false);
+ UserHandle.getCallingUserId(), false);
IBinder ret = null;
if (r != null) {
@@ -759,8 +759,8 @@
}
r = new ServiceRecord(mAm, ss, name, filter, sInfo, res);
res.setService(r);
- mServiceMap.putServiceByName(name, UserId.getUserId(r.appInfo.uid), r);
- mServiceMap.putServiceByIntent(filter, UserId.getUserId(r.appInfo.uid), r);
+ mServiceMap.putServiceByName(name, UserHandle.getUserId(r.appInfo.uid), r);
+ mServiceMap.putServiceByIntent(filter, UserHandle.getUserId(r.appInfo.uid), r);
// Make sure this component isn't in the pending list.
int N = mPendingServices.size();
@@ -1725,7 +1725,7 @@
ArrayList<ActivityManager.RunningServiceInfo> res
= new ArrayList<ActivityManager.RunningServiceInfo>();
- int userId = UserId.getUserId(Binder.getCallingUid());
+ int userId = UserHandle.getUserId(Binder.getCallingUid());
if (mServiceMap.getAllServices(userId).size() > 0) {
Iterator<ServiceRecord> it
= mServiceMap.getAllServices(userId).iterator();
@@ -1746,7 +1746,7 @@
}
public PendingIntent getRunningServiceControlPanelLocked(ComponentName name) {
- int userId = UserId.getUserId(Binder.getCallingUid());
+ int userId = UserHandle.getUserId(Binder.getCallingUid());
ServiceRecord r = mServiceMap.getServiceByName(name, userId);
if (r != null) {
for (ArrayList<ConnectionRecord> conn : r.connections.values()) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 0c378c9..d36114c 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -111,7 +111,7 @@
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.os.UserId;
+import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.text.format.Time;
@@ -1084,7 +1084,7 @@
boolean restart = (msg.arg2 == 1);
String pkg = (String) msg.obj;
forceStopPackageLocked(pkg, uid, restart, false, true, false,
- UserId.getUserId(uid));
+ UserHandle.getUserId(uid));
}
} break;
case FINALIZE_PENDING_INTENT_MSG: {
@@ -1829,7 +1829,7 @@
if (procs == null) return null;
final int N = procs.size();
for (int i = 0; i < N; i++) {
- if (UserId.isSameUser(procs.keyAt(i), uid)) return procs.valueAt(i);
+ if (UserHandle.isSameUser(procs.keyAt(i), uid)) return procs.valueAt(i);
}
}
ProcessRecord proc = mProcessNames.get(processName, uid);
@@ -1980,10 +1980,20 @@
int uid = app.uid;
int[] gids = null;
+ int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
if (!app.isolated) {
try {
- gids = mContext.getPackageManager().getPackageGids(
- app.info.packageName);
+ final PackageManager pm = mContext.getPackageManager();
+ gids = pm.getPackageGids(app.info.packageName);
+ if (pm.checkPermission(
+ android.Manifest.permission.READ_EXTERNAL_STORAGE, app.info.packageName)
+ == PERMISSION_GRANTED) {
+ if (Environment.isExternalStorageEmulated()) {
+ mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER;
+ } else {
+ mountExternal = Zygote.MOUNT_EXTERNAL_SINGLEUSER;
+ }
+ }
} catch (PackageManager.NameNotFoundException e) {
Slog.w(TAG, "Unable to retrieve gids", e);
}
@@ -2025,7 +2035,7 @@
// Start the process. It will either succeed and return a result containing
// the PID of the new process, or else throw a RuntimeException.
Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
- app.processName, uid, uid, gids, debugFlags,
+ app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, null, null);
BatteryStatsImpl bs = app.batteryStats.getBatteryStats();
@@ -2195,7 +2205,7 @@
}
void enforceNotIsolatedCaller(String caller) {
- if (UserId.isIsolated(Binder.getCallingUid())) {
+ if (UserHandle.isIsolated(Binder.getCallingUid())) {
throw new SecurityException("Isolated process not allowed to call " + caller);
}
}
@@ -2324,7 +2334,7 @@
String resultWho, int requestCode, int startFlags,
String profileFile, ParcelFileDescriptor profileFd, Bundle options) {
return startActivityAsUser(caller, intent, resolvedType, resultTo, resultWho, requestCode,
- startFlags, profileFile, profileFd, options, UserId.getCallingUserId());
+ startFlags, profileFile, profileFd, options, UserHandle.getCallingUserId());
}
public final int startActivityAsUser(IApplicationThread caller,
@@ -2332,20 +2342,20 @@
String resultWho, int requestCode, int startFlags,
String profileFile, ParcelFileDescriptor profileFd, Bundle options, int userId) {
enforceNotIsolatedCaller("startActivity");
- if (userId != UserId.getCallingUserId()) {
+ if (userId != UserHandle.getCallingUserId()) {
// Requesting a different user, make sure that they have the permission
if (checkComponentPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
Binder.getCallingPid(), Binder.getCallingUid(), -1, true)
== PackageManager.PERMISSION_GRANTED) {
// Translate to the current user id, if caller wasn't aware
- if (userId == UserId.USER_CURRENT) {
+ if (userId == UserHandle.USER_CURRENT) {
userId = mCurrentUserId;
}
} else {
String msg = "Permission Denial: "
+ "Request to startActivity as user " + userId
- + " but is calling from user " + UserId.getCallingUserId()
+ + " but is calling from user " + UserHandle.getCallingUserId()
+ "; this requires "
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
Slog.w(TAG, msg);
@@ -2457,7 +2467,7 @@
AppGlobals.getPackageManager().queryIntentActivities(
intent, r.resolvedType,
PackageManager.MATCH_DEFAULT_ONLY | STOCK_PM_FLAGS,
- UserId.getCallingUserId());
+ UserHandle.getCallingUserId());
// Look for the original activity in the list...
final int N = resolves != null ? resolves.size() : 0;
@@ -2562,7 +2572,7 @@
"startActivityInPackage only available to the system");
}
int ret = mMainStack.startActivities(null, uid, intents, resolvedTypes, resultTo,
- options, UserId.getUserId(uid));
+ options, UserHandle.getUserId(uid));
return ret;
}
@@ -3428,7 +3438,7 @@
throw new SecurityException(msg);
}
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
long callingId = Binder.clearCallingIdentity();
try {
IPackageManager pm = AppGlobals.getPackageManager();
@@ -3502,7 +3512,7 @@
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
- final int userId = UserId.getCallingUserId();
+ final int userId = UserHandle.getCallingUserId();
long callingId = Binder.clearCallingIdentity();
try {
IPackageManager pm = AppGlobals.getPackageManager();
@@ -3638,7 +3648,7 @@
}
private void forceStopPackageLocked(final String packageName, int uid) {
- forceStopPackageLocked(packageName, uid, false, false, true, false, UserId.getUserId(uid));
+ forceStopPackageLocked(packageName, uid, false, false, true, false, UserHandle.getUserId(uid));
Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,
Uri.fromParts("package", packageName, null));
if (!mProcessesReady) {
@@ -3648,7 +3658,7 @@
broadcastIntentLocked(null, null, intent,
null, null, 0, null, null, null,
false, false,
- MY_PID, Process.SYSTEM_UID, UserId.getUserId(uid));
+ MY_PID, Process.SYSTEM_UID, UserHandle.getUserId(uid));
}
private final boolean killPackageProcessesLocked(String packageName, int uid,
@@ -4369,8 +4379,8 @@
try {
if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
int uid = AppGlobals.getPackageManager()
- .getPackageUid(packageName, UserId.getUserId(callingUid));
- if (!UserId.isSameApp(callingUid, uid)) {
+ .getPackageUid(packageName, UserHandle.getUserId(callingUid));
+ if (!UserHandle.isSameApp(callingUid, uid)) {
String msg = "Permission Denial: getIntentSender() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
@@ -4466,8 +4476,8 @@
PendingIntentRecord rec = (PendingIntentRecord)sender;
try {
int uid = AppGlobals.getPackageManager()
- .getPackageUid(rec.key.packageName, UserId.getCallingUserId());
- if (!UserId.isSameApp(uid, Binder.getCallingUid())) {
+ .getPackageUid(rec.key.packageName, UserHandle.getCallingUserId());
+ if (!UserHandle.isSameApp(uid, Binder.getCallingUid())) {
String msg = "Permission Denial: cancelIntentSender() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
@@ -4686,7 +4696,7 @@
if (permission == null) {
return PackageManager.PERMISSION_DENIED;
}
- return checkComponentPermission(permission, pid, UserId.getAppId(uid), -1, true);
+ return checkComponentPermission(permission, pid, UserHandle.getAppId(uid), -1, true);
}
/**
@@ -4696,7 +4706,7 @@
int checkCallingPermission(String permission) {
return checkPermission(permission,
Binder.getCallingPid(),
- UserId.getAppId(Binder.getCallingUid()));
+ UserHandle.getAppId(Binder.getCallingUid()));
}
/**
@@ -4827,7 +4837,7 @@
pid = tlsIdentity.pid;
}
- uid = UserId.getAppId(uid);
+ uid = UserHandle.getAppId(uid);
// Our own process gets to do everything.
if (pid == MY_PID) {
return PackageManager.PERMISSION_GRANTED;
@@ -4873,13 +4883,13 @@
String name = uri.getAuthority();
ProviderInfo pi = null;
ContentProviderRecord cpr = mProviderMap.getProviderByName(name,
- UserId.getUserId(callingUid));
+ UserHandle.getUserId(callingUid));
if (cpr != null) {
pi = cpr.info;
} else {
try {
pi = pm.resolveContentProvider(name,
- PackageManager.GET_URI_PERMISSION_PATTERNS, UserId.getUserId(callingUid));
+ PackageManager.GET_URI_PERMISSION_PATTERNS, UserHandle.getUserId(callingUid));
} catch (RemoteException ex) {
}
}
@@ -4891,7 +4901,7 @@
int targetUid = lastTargetUid;
if (targetUid < 0 && targetPkg != null) {
try {
- targetUid = pm.getPackageUid(targetPkg, UserId.getUserId(callingUid));
+ targetUid = pm.getPackageUid(targetPkg, UserHandle.getUserId(callingUid));
if (targetUid < 0) {
if (DEBUG_URI_PERMISSION) Slog.v(TAG,
"Can't grant URI permission no uid for: " + targetPkg);
@@ -5183,7 +5193,7 @@
final String authority = uri.getAuthority();
ProviderInfo pi = null;
- int userId = UserId.getUserId(callingUid);
+ int userId = UserHandle.getUserId(callingUid);
ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, userId);
if (cpr != null) {
pi = cpr.info;
@@ -5521,7 +5531,7 @@
public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum,
int flags, int userId) {
final int callingUid = Binder.getCallingUid();
- if (userId != UserId.getCallingUserId()) {
+ if (userId != UserHandle.getCallingUserId()) {
// Check if the caller is holding permissions for cross-user requests.
if (checkComponentPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
@@ -5529,13 +5539,13 @@
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: "
+ "Request to get recent tasks for user " + userId
- + " but is calling from user " + UserId.getUserId(callingUid)
+ + " but is calling from user " + UserHandle.getUserId(callingUid)
+ "; this requires "
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
Slog.w(TAG, msg);
throw new SecurityException(msg);
} else {
- if (userId == UserId.USER_CURRENT) {
+ if (userId == UserHandle.USER_CURRENT) {
userId = mCurrentUserId;
}
}
@@ -6006,7 +6016,7 @@
(ProviderInfo)providers.get(i);
boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
cpi.name, cpi.flags);
- if (singleton && UserId.getUserId(app.uid) != 0) {
+ if (singleton && UserHandle.getUserId(app.uid) != 0) {
// This is a singleton provider, but a user besides the
// default user is asking to initialize a process it runs
// in... well, no, it doesn't actually run in this process,
@@ -6177,7 +6187,7 @@
}
// First check if this content provider has been published...
- int userId = UserId.getUserId(r != null ? r.uid : Binder.getCallingUid());
+ int userId = UserHandle.getUserId(r != null ? r.uid : Binder.getCallingUid());
cpr = mProviderMap.getProviderByName(name, userId);
boolean providerRunning = cpr != null;
if (providerRunning) {
@@ -6725,7 +6735,7 @@
BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
int uid = info.uid;
if (isolated) {
- int userId = UserId.getUserId(uid);
+ int userId = UserHandle.getUserId(uid);
int stepsLeft = Process.LAST_ISOLATED_UID - Process.FIRST_ISOLATED_UID + 1;
uid = 0;
while (true) {
@@ -6733,7 +6743,7 @@
|| mNextIsolatedProcessUid > Process.LAST_ISOLATED_UID) {
mNextIsolatedProcessUid = Process.FIRST_ISOLATED_UID;
}
- uid = UserId.getUid(userId, mNextIsolatedProcessUid);
+ uid = UserHandle.getUid(userId, mNextIsolatedProcessUid);
mNextIsolatedProcessUid++;
if (mIsolatedProcesses.indexOfKey(uid) < 0) {
// No process for this uid, use it.
@@ -6771,7 +6781,7 @@
// This package really, really can not be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
- info.packageName, false, UserId.getUserId(app.uid));
+ info.packageName, false, UserHandle.getUserId(app.uid));
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
@@ -8509,7 +8519,7 @@
IPackageManager pm = AppGlobals.getPackageManager();
for (String pkg : extList) {
try {
- ApplicationInfo info = pm.getApplicationInfo(pkg, 0, UserId.getCallingUserId());
+ ApplicationInfo info = pm.getApplicationInfo(pkg, 0, UserHandle.getCallingUserId());
if ((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
retList.add(info);
}
@@ -10258,10 +10268,10 @@
cpr.launchingApp = null;
cpr.notifyAll();
}
- mProviderMap.removeProviderByClass(cpr.name, UserId.getUserId(cpr.uid));
+ mProviderMap.removeProviderByClass(cpr.name, UserHandle.getUserId(cpr.uid));
String names[] = cpr.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
- mProviderMap.removeProviderByName(names[j], UserId.getUserId(cpr.uid));
+ mProviderMap.removeProviderByName(names[j], UserHandle.getUserId(cpr.uid));
}
}
@@ -10599,7 +10609,7 @@
boolean isSingleton(String componentProcessName, ApplicationInfo aInfo,
String className, int flags) {
boolean result = false;
- if (UserId.getAppId(aInfo.uid) >= Process.FIRST_APPLICATION_UID) {
+ if (UserHandle.getAppId(aInfo.uid) >= Process.FIRST_APPLICATION_UID) {
if ((flags&ServiceInfo.FLAG_SINGLE_USER) != 0) {
if (ActivityManager.checkUidPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS,
@@ -10704,7 +10714,7 @@
// Backup agent is now in use, its package can't be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
- app.packageName, false, UserId.getUserId(app.uid));
+ app.packageName, false, UserHandle.getUserId(app.uid));
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
@@ -11046,7 +11056,7 @@
// If the caller is trying to send this broadcast to a different
// user, verify that is allowed.
- if (UserId.getUserId(callingUid) != userId) {
+ if (UserHandle.getUserId(callingUid) != userId) {
if (checkComponentPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
callingPid, callingUid, -1, true)
@@ -11060,7 +11070,7 @@
String msg = "Permission Denial: " + intent.getAction()
+ " broadcast from " + callerPackage
+ " asks to send as user " + userId
- + " but is calling from user " + UserId.getUserId(callingUid)
+ + " but is calling from user " + UserHandle.getUserId(callingUid)
+ "; this requires "
+ android.Manifest.permission.INTERACT_ACROSS_USERS;
Slog.w(TAG, msg);
@@ -11553,7 +11563,7 @@
throw new SecurityException(msg);
}
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
final long origId = Binder.clearCallingIdentity();
// Instrumentation can kill and relaunch even persistent processes
forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, userId);
@@ -11616,7 +11626,7 @@
public void finishInstrumentation(IApplicationThread target,
int resultCode, Bundle results) {
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
// Refuse possible leaked file descriptors
if (results != null && results.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -11929,7 +11939,7 @@
} else {
try {
ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
- destIntent.getComponent(), 0, UserId.getCallingUserId());
+ destIntent.getComponent(), 0, UserHandle.getCallingUserId());
int res = mMainStack.startActivityLocked(srec.app.thread, destIntent,
null, aInfo, parent.appToken, null,
0, -1, parent.launchedFromUid, 0, null, true, null);
@@ -13482,7 +13492,7 @@
// Inform of user switch
Intent addedIntent = new Intent(Intent.ACTION_USER_SWITCHED);
- addedIntent.putExtra(Intent.EXTRA_USERID, userId);
+ addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_ACCOUNTS);
return true;
@@ -13499,7 +13509,7 @@
}
private void onUserRemoved(Intent intent) {
- int extraUserId = intent.getIntExtra(Intent.EXTRA_USERID, -1);
+ int extraUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
if (extraUserId < 1) return;
// Kill all the processes for the user
@@ -13509,7 +13519,7 @@
for (Entry<String, SparseArray<ProcessRecord>> uidMap : map.entrySet()) {
SparseArray<ProcessRecord> uids = uidMap.getValue();
for (int i = 0; i < uids.size(); i++) {
- if (UserId.getUserId(uids.keyAt(i)) == extraUserId) {
+ if (UserHandle.getUserId(uids.keyAt(i)) == extraUserId) {
pkgAndUids.add(new Pair<String,Integer>(uidMap.getKey(), uids.keyAt(i)));
}
}
@@ -13535,14 +13545,14 @@
}
private void checkValidCaller(int uid, int userId) {
- if (UserId.getUserId(uid) == userId || uid == Process.SYSTEM_UID || uid == 0) return;
+ if (UserHandle.getUserId(uid) == userId || uid == Process.SYSTEM_UID || uid == 0) return;
throw new SecurityException("Caller uid=" + uid
+ " is not privileged to communicate with user=" + userId);
}
private int applyUserId(int uid, int userId) {
- return UserId.getUid(userId, uid);
+ return UserHandle.getUid(userId, uid);
}
ApplicationInfo getAppInfoForUser(ApplicationInfo info, int userId) {
@@ -13556,7 +13566,7 @@
ActivityInfo getActivityInfoForUser(ActivityInfo aInfo, int userId) {
if (aInfo == null
- || (userId < 1 && aInfo.applicationInfo.uid < UserId.PER_USER_RANGE)) {
+ || (userId < 1 && aInfo.applicationInfo.uid < UserHandle.PER_USER_RANGE)) {
return aInfo;
}
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index cef2e47..c70650d 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -37,7 +37,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
@@ -321,7 +321,7 @@
appToken = new Token(this);
info = aInfo;
launchedFromUid = _launchedFromUid;
- userId = UserId.getUserId(aInfo.applicationInfo.uid);
+ userId = UserHandle.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 196a259..1e0827f 100755
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -55,7 +55,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
@@ -501,7 +501,7 @@
TaskRecord cp = null;
- final int userId = UserId.getUserId(info.applicationInfo.uid);
+ final int userId = UserHandle.getUserId(info.applicationInfo.uid);
final int N = mHistory.size();
for (int i=(N-1); i>=0; i--) {
ActivityRecord r = mHistory.get(i);
@@ -545,7 +545,7 @@
if (info.targetActivity != null) {
cls = new ComponentName(info.packageName, info.targetActivity);
}
- final int userId = UserId.getUserId(info.applicationInfo.uid);
+ final int userId = UserHandle.getUserId(info.applicationInfo.uid);
final int N = mHistory.size();
for (int i=(N-1); i>=0; i--) {
@@ -2406,7 +2406,7 @@
}
if (err == ActivityManager.START_SUCCESS) {
- final int userId = aInfo != null ? UserId.getUserId(aInfo.applicationInfo.uid) : 0;
+ final int userId = aInfo != null ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;
Slog.i(TAG, "START {" + intent.toShortString(true, true, true, false)
+ " u=" + userId + "} from pid " + (callerApp != null ? callerApp.pid : callingPid));
}
diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java
index 76ddb96..7873dd8 100644
--- a/services/java/com/android/server/am/BroadcastQueue.java
+++ b/services/java/com/android/server/am/BroadcastQueue.java
@@ -36,7 +36,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.EventLog;
import android.util.Slog;
@@ -373,7 +373,7 @@
BroadcastFilter filter, boolean ordered) {
boolean skip = false;
if (r.onlySendToCaller) {
- if (!UserId.isSameApp(r.callingUid, filter.owningUid)) {
+ if (!UserHandle.isSameApp(r.callingUid, filter.owningUid)) {
Slog.w(TAG, "Permission Denial: broadcasting "
+ r.intent.toString()
+ " from " + r.callerPackage + " (pid="
@@ -668,7 +668,7 @@
boolean skip = false;
if (r.onlySendToCaller) {
- if (!UserId.isSameApp(r.callingUid, info.activityInfo.applicationInfo.uid)) {
+ if (!UserHandle.isSameApp(r.callingUid, info.activityInfo.applicationInfo.uid)) {
Slog.w(TAG, "Permission Denial: broadcasting "
+ r.intent.toString()
+ " from " + r.callerPackage + " (pid="
@@ -766,7 +766,7 @@
info.activityInfo = mService.getActivityInfoForUser(info.activityInfo, 0);
}
r.curReceiver = info.activityInfo;
- if (DEBUG_MU && r.callingUid > UserId.PER_USER_RANGE) {
+ if (DEBUG_MU && r.callingUid > UserHandle.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);
@@ -775,7 +775,7 @@
// Broadcast is being executed, its package can't be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
- r.curComponent.getPackageName(), false, UserId.getUserId(r.callingUid));
+ r.curComponent.getPackageName(), false, UserHandle.getUserId(r.callingUid));
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index ad15da1..d3b8510 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -25,7 +25,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.Slog;
import java.io.PrintWriter;
@@ -259,7 +259,7 @@
owner.broadcastIntentInPackage(key.packageName, uid,
finalIntent, resolvedType,
finishedReceiver, code, null, null,
- requiredPermission, (finishedReceiver != null), false, UserId
+ requiredPermission, (finishedReceiver != null), false, UserHandle
.getUserId(uid));
sendFinish = false;
} catch (RuntimeException e) {
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 42b7708..d372422 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -30,7 +30,7 @@
import android.os.IBinder;
import android.os.Process;
import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.PrintWriterPrinter;
import android.util.TimeUtils;
@@ -313,7 +313,7 @@
info = _info;
isolated = _info.uid != _uid;
uid = _uid;
- userId = UserId.getUserId(_uid);
+ userId = UserHandle.getUserId(_uid);
processName = _processName;
pkgList.add(_info.packageName);
thread = _thread;
@@ -396,7 +396,7 @@
sb.append(info.uid%Process.FIRST_APPLICATION_UID);
if (uid != info.uid) {
sb.append('i');
- sb.append(UserId.getAppId(uid) - Process.FIRST_ISOLATED_UID);
+ sb.append(UserHandle.getAppId(uid) - Process.FIRST_ISOLATED_UID);
}
}
}
diff --git a/services/java/com/android/server/am/ProviderMap.java b/services/java/com/android/server/am/ProviderMap.java
index 405718f..ab2e428 100644
--- a/services/java/com/android/server/am/ProviderMap.java
+++ b/services/java/com/android/server/am/ProviderMap.java
@@ -20,7 +20,7 @@
import android.os.Binder;
import android.os.Process;
import android.os.RemoteException;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.Slog;
import android.util.SparseArray;
@@ -98,7 +98,7 @@
if (record.singleton) {
mSingletonByName.put(name, record);
} else {
- final int userId = UserId.getUserId(record.appInfo.uid);
+ final int userId = UserHandle.getUserId(record.appInfo.uid);
getProvidersByName(userId).put(name, record);
}
}
@@ -111,7 +111,7 @@
if (record.singleton) {
mSingletonByClass.put(name, record);
} else {
- final int userId = UserId.getUserId(record.appInfo.uid);
+ final int userId = UserHandle.getUserId(record.appInfo.uid);
getProvidersByClass(userId).put(name, record);
}
}
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 828eef7..41386e4 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -31,7 +31,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.Slog;
import android.util.TimeUtils;
@@ -296,7 +296,7 @@
this.restarter = restarter;
createTime = SystemClock.elapsedRealtime();
lastActivity = SystemClock.uptimeMillis();
- userId = UserId.getUserId(appInfo.uid);
+ userId = UserHandle.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 3a767c2..1bae9ca 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -19,7 +19,7 @@
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.Slog;
import java.io.PrintWriter;
@@ -101,7 +101,7 @@
}
if (info.applicationInfo != null) {
- userId = UserId.getUserId(info.applicationInfo.uid);
+ userId = UserHandle.getUserId(info.applicationInfo.uid);
}
}
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index 3cd767d..8d7e92a 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -845,6 +845,9 @@
}
}
}
+
+ // save current uids
+ mClientUids = uids;
}
@Override
diff --git a/services/java/com/android/server/location/LocationFudger.java b/services/java/com/android/server/location/LocationFudger.java
new file mode 100644
index 0000000..57bc1c5
--- /dev/null
+++ b/services/java/com/android/server/location/LocationFudger.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.security.SecureRandom;
+import android.location.Location;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.util.Log;
+
+
+/**
+ * Contains the logic to obfuscate (fudge) locations for coarse applications.
+ *
+ * <p>The goal is just to prevent applications with only
+ * the coarse location permission from receiving a fine location.
+ */
+public class LocationFudger {
+ private static final boolean D = false;
+ private static final String TAG = "LocationFudge";
+
+ private static final String EXTRA_COARSE_LOCATION = "coarseLocation";
+
+ /**
+ * This is the main control: Best location accuracy allowed for coarse applications.
+ */
+ private static final float ACCURACY_METERS = 200.0f;
+
+ /**
+ * The distance between grids for snap-to-grid. See {@link #createCoarse}.
+ */
+ private static final double GRID_SIZE_METERS = ACCURACY_METERS;
+
+ /**
+ * Standard deviation of the (normally distributed) random offset applied
+ * to coarse locations. It does not need to be as large as
+ * {@link #COARSE_ACCURACY_METERS} because snap-to-grid is the primary obfuscation
+ * method. See further details in the implementation.
+ */
+ private static final double STANDARD_DEVIATION_METERS = GRID_SIZE_METERS / 4.0;
+
+ /**
+ * This is the fastest interval that applications can receive coarse
+ * locations.
+ */
+ public static final long FASTEST_INTERVAL_MS = 10 * 60 * 1000; // 10 minutes
+
+ /**
+ * The duration until we change the random offset.
+ */
+ private static final long CHANGE_INTERVAL_MS = 60 * 60 * 1000; // 1 hour
+
+ /**
+ * The percentage that we change the random offset at every interval.
+ *
+ * <p>0.0 indicates the random offset doesn't change. 1.0
+ * indicates the random offset is completely replaced every interval.
+ */
+ private static final double CHANGE_PER_INTERVAL = 0.03; // 3% change
+
+ // Pre-calculated weights used to move the random offset.
+ //
+ // The goal is to iterate on the previous offset, but keep
+ // the resulting standard deviation the same. The variance of
+ // two gaussian distributions summed together is equal to the
+ // sum of the variance of each distribution. So some quick
+ // algebra results in the following sqrt calculation to
+ // weigh in a new offset while keeping the final standard
+ // deviation unchanged.
+ private static final double NEW_WEIGHT = CHANGE_PER_INTERVAL;
+ private static final double PREVIOUS_WEIGHT = Math.sqrt(1 - NEW_WEIGHT * NEW_WEIGHT);
+
+ /**
+ * This number actually varies because the earth is not round, but
+ * 111,000 meters is considered generally acceptable.
+ */
+ private static final int APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR = 111000;
+
+ /**
+ * Maximum latitude.
+ *
+ * <p>We pick a value 1 meter away from 90.0 degrees in order
+ * to keep cosine(MAX_LATITUDE) to a non-zero value, so that we avoid
+ * divide by zero fails.
+ */
+ private static final double MAX_LATITUDE = 90.0 -
+ (1.0 / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR);
+
+ private final Object mLock = new Object();
+ private final SecureRandom mRandom = new SecureRandom();
+
+ // all fields below protected by mLock
+ private double mOffsetLatitudeMeters;
+ private double mOffsetLongitudeMeters;
+ private long mNextInterval;
+
+ public LocationFudger() {
+ mOffsetLatitudeMeters = nextOffset();
+ mOffsetLongitudeMeters = nextOffset();
+ mNextInterval = SystemClock.elapsedRealtime() + CHANGE_INTERVAL_MS;
+ }
+
+ /**
+ * Get the cached coarse location, or generate a new one and cache it.
+ */
+ public Location getOrCreate(Location location) {
+ Bundle extras = location.getExtras();
+ if (extras == null) {
+ return addCoarseLocationExtra(location);
+ }
+ Parcelable parcel = extras.getParcelable(EXTRA_COARSE_LOCATION);
+ if (parcel == null) {
+ return addCoarseLocationExtra(location);
+ }
+ if (!(parcel instanceof Location)) {
+ return addCoarseLocationExtra(location);
+ }
+ Location coarse = (Location) parcel;
+ if (coarse.getAccuracy() < ACCURACY_METERS) {
+ return addCoarseLocationExtra(location);
+ }
+ return coarse;
+ }
+
+ private Location addCoarseLocationExtra(Location location) {
+ Bundle extras = location.getExtras();
+ if (extras == null) extras = new Bundle();
+ Location coarse = createCoarse(location);
+ extras.putParcelable(EXTRA_COARSE_LOCATION, coarse);
+ location.setExtras(extras);
+ return coarse;
+ }
+
+ /**
+ * Create a coarse location.
+ *
+ * <p>Two techniques are used: random offsets and snap-to-grid.
+ *
+ * <p>First we add a random offset. This mitigates against detecting
+ * grid transitions. Without a random offset it is possible to detect
+ * a users position very accurately when they cross a grid boundary.
+ * The random offset changes very slowly over time, to mitigate against
+ * taking many location samples and averaging them out.
+ *
+ * <p>Second we snap-to-grid (quantize). This has the nice property of
+ * producing stable results, and mitigating against taking many samples
+ * to average out a random offset.
+ */
+ private Location createCoarse(Location fine) {
+ Location coarse = new Location(fine);
+
+ // clean all the optional information off the location, because
+ // this can leak detailed location information
+ coarse.removeBearing();
+ coarse.removeSpeed();
+ coarse.removeAltitude();
+ coarse.setExtras(null);
+
+ double lat = coarse.getLatitude();
+ double lon = coarse.getLongitude();
+
+ // wrap
+ lat = wrapLatitude(lat);
+ lon = wrapLongitude(lon);
+
+ // Step 1) apply a random offset
+ //
+ // The goal of the random offset is to prevent the application
+ // from determining that the device is on a grid boundary
+ // when it crosses from one grid to the next.
+ //
+ // We apply the offset even if the location already claims to be
+ // inaccurate, because it may be more accurate than claimed.
+ synchronized (mLock) {
+ updateRandomOffsetLocked();
+ // perform lon first whilst lat is still within bounds
+ lon += metersToDegreesLongitude(mOffsetLongitudeMeters, lat);
+ lat += metersToDegreesLatitude(mOffsetLatitudeMeters);
+ if (D) Log.d(TAG, String.format("applied offset of %.0f, %.0f (meters)",
+ mOffsetLongitudeMeters, mOffsetLatitudeMeters));
+ }
+
+ // wrap
+ lat = wrapLatitude(lat);
+ lon = wrapLongitude(lon);
+
+ // Step 2) Snap-to-grid (quantize)
+ //
+ // This is the primary means of obfuscation. It gives nice consistent
+ // results and is very effective at hiding the true location
+ // (as long as you are not sitting on a grid boundary, which
+ // step 1 mitigates).
+ //
+ // Note we quantize the latitude first, since the longitude
+ // quantization depends on the latitude value and so leaks information
+ // about the latitude
+ double latGranularity = metersToDegreesLatitude(GRID_SIZE_METERS);
+ lat = Math.round(lat / latGranularity) * latGranularity;
+ double lonGranularity = metersToDegreesLongitude(GRID_SIZE_METERS, lat);
+ lon = Math.round(lon / lonGranularity) * lonGranularity;
+
+ // wrap again
+ lat = wrapLatitude(lat);
+ lon = wrapLongitude(lon);
+
+ // apply
+ coarse.setLatitude(lat);
+ coarse.setLongitude(lon);
+ coarse.setAccuracy(Math.max(ACCURACY_METERS, coarse.getAccuracy()));
+
+ if (D) Log.d(TAG, "fudged " + fine + " to " + coarse);
+ return coarse;
+ }
+
+ /**
+ * Update the random offset over time.
+ *
+ * <p>If the random offset was new for every location
+ * fix then an application can more easily average location results
+ * over time,
+ * especially when the location is near a grid boundary. On the
+ * other hand if the random offset is constant then if an application
+ * found a way to reverse engineer the offset they would be able
+ * to detect location at grid boundaries very accurately. So
+ * we choose a random offset and then very slowly move it, to
+ * make both approaches very hard.
+ *
+ * <p>The random offset does not need to be large, because snap-to-grid
+ * is the primary obfuscation mechanism. It just needs to be large
+ * enough to stop information leakage as we cross grid boundaries.
+ */
+ private void updateRandomOffsetLocked() {
+ long now = SystemClock.elapsedRealtime();
+ if (now < mNextInterval) {
+ return;
+ }
+
+ if (D) Log.d(TAG, String.format("old offset: %.0f, %.0f (meters)",
+ mOffsetLongitudeMeters, mOffsetLatitudeMeters));
+
+ // ok, need to update the random offset
+ mNextInterval = now + CHANGE_INTERVAL_MS;
+
+ mOffsetLatitudeMeters *= PREVIOUS_WEIGHT;
+ mOffsetLatitudeMeters += NEW_WEIGHT * nextOffset();
+ mOffsetLongitudeMeters *= PREVIOUS_WEIGHT;
+ mOffsetLongitudeMeters += NEW_WEIGHT * nextOffset();
+
+ if (D) Log.d(TAG, String.format("new offset: %.0f, %.0f (meters)",
+ mOffsetLongitudeMeters, mOffsetLatitudeMeters));
+ }
+
+ private double nextOffset() {
+ return mRandom.nextGaussian() * STANDARD_DEVIATION_METERS;
+ }
+
+ private static double wrapLatitude(double lat) {
+ if (lat > MAX_LATITUDE) {
+ lat = MAX_LATITUDE;
+ }
+ if (lat < -MAX_LATITUDE) {
+ lat = -MAX_LATITUDE;
+ }
+ return lat;
+ }
+
+ private static double wrapLongitude(double lon) {
+ lon %= 360.0; // wraps into range (-360.0, +360.0)
+ if (lon >= 180.0) {
+ lon -= 360.0;
+ }
+ if (lon < -180.0) {
+ lon += 360.0;
+ }
+ return lon;
+ }
+
+ private static double metersToDegreesLatitude(double distance) {
+ return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR;
+ }
+
+ /**
+ * Requires latitude since longitudinal distances change with distance from equator.
+ */
+ private static double metersToDegreesLongitude(double distance, double lat) {
+ return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR / Math.cos(Math.toRadians(lat));
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println(String.format("offset: %.0f, %.0f (meters)", mOffsetLongitudeMeters,
+ mOffsetLatitudeMeters));
+ }
+}
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 46c24b0..a7cba5a 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -112,7 +112,7 @@
import android.os.MessageQueue.IdleHandler;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
-import android.os.UserId;
+import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.telephony.TelephonyManager;
@@ -426,7 +426,7 @@
final String action = intent.getAction();
final int uid = intent.getIntExtra(EXTRA_UID, 0);
- final int appId = UserId.getAppId(uid);
+ final int appId = UserHandle.getAppId(uid);
synchronized (mRulesLock) {
if (ACTION_PACKAGE_ADDED.equals(action)) {
// NOTE: PACKAGE_ADDED is currently only sent once, and is
@@ -1188,8 +1188,8 @@
final int uid = readIntAttribute(in, ATTR_UID);
final int policy = readIntAttribute(in, ATTR_POLICY);
- final int appId = UserId.getAppId(uid);
- if (UserId.isApp(appId)) {
+ final int appId = UserHandle.getAppId(uid);
+ if (UserHandle.isApp(appId)) {
setAppPolicyUnchecked(appId, policy, false);
} else {
Slog.w(TAG, "unable to apply policy to UID " + uid + "; ignoring");
@@ -1198,7 +1198,7 @@
final int appId = readIntAttribute(in, ATTR_APP_ID);
final int policy = readIntAttribute(in, ATTR_POLICY);
- if (UserId.isApp(appId)) {
+ if (UserHandle.isApp(appId)) {
setAppPolicyUnchecked(appId, policy, false);
} else {
Slog.w(TAG, "unable to apply policy to appId " + appId + "; ignoring");
@@ -1304,7 +1304,7 @@
public void setAppPolicy(int appId, int policy) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
- if (!UserId.isApp(appId)) {
+ if (!UserHandle.isApp(appId)) {
throw new IllegalArgumentException("cannot apply policy to appId " + appId);
}
@@ -1698,7 +1698,7 @@
final PackageManager pm = mContext.getPackageManager();
final List<ApplicationInfo> apps = pm.getInstalledApplications(0);
for (ApplicationInfo app : apps) {
- final int appId = UserId.getAppId(app.uid);
+ final int appId = UserHandle.getAppId(app.uid);
updateRulesForAppLocked(appId);
}
@@ -1710,7 +1710,7 @@
private void updateRulesForAppLocked(int appId) {
UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
for (UserInfo user : um.getUsers()) {
- final int uid = UserId.getUid(user.id, appId);
+ final int uid = UserHandle.getUid(user.id, appId);
updateRulesForUidLocked(uid);
}
}
@@ -1718,7 +1718,7 @@
private static boolean isUidValidForRules(int uid) {
// allow rules on specific system services, and any apps
if (uid == android.os.Process.MEDIA_UID || uid == android.os.Process.DRM_UID
- || UserId.isApp(uid)) {
+ || UserHandle.isApp(uid)) {
return true;
}
@@ -1728,7 +1728,7 @@
private void updateRulesForUidLocked(int uid) {
if (!isUidValidForRules(uid)) return;
- final int appId = UserId.getAppId(uid);
+ final int appId = UserHandle.getAppId(uid);
final int appPolicy = getAppPolicy(appId);
final boolean uidForeground = isUidForeground(uid);
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 4befb9e..0d6e08d 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -97,10 +97,11 @@
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
+import android.os.SELinux;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.os.UserId;
+import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings.Secure;
import android.security.SystemKeyStore;
@@ -145,6 +146,7 @@
import libcore.io.ErrnoException;
import libcore.io.IoUtils;
import libcore.io.Libcore;
+import libcore.io.StructStat;
/**
* Keep track of all those .apks everywhere.
@@ -304,8 +306,6 @@
File mScanningPath;
int mLastScanError;
- final int[] mOutPermissions = new int[3];
-
// ----------------------------------------------------------------
// Keys are String (package name), values are Package. This also serves
@@ -691,15 +691,15 @@
}
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
res.pkg.applicationInfo.packageName,
- extras, null, null, UserId.USER_ALL);
+ extras, null, null, UserHandle.USER_ALL);
if (update) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
res.pkg.applicationInfo.packageName,
- extras, null, null, UserId.USER_ALL);
+ extras, null, null, UserHandle.USER_ALL);
sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
null, null,
res.pkg.applicationInfo.packageName, null,
- UserId.USER_ALL);
+ UserHandle.USER_ALL);
}
if (res.removedInfo.args != null) {
// Remove the replaced package's older resources safely now
@@ -1630,14 +1630,14 @@
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
if(p != null) {
- return UserId.getUid(userId, p.applicationInfo.uid);
+ return UserHandle.getUid(userId, p.applicationInfo.uid);
}
PackageSetting ps = mSettings.mPackages.get(packageName);
if((ps == null) || (ps.pkg == null) || (ps.pkg.applicationInfo == null)) {
return -1;
}
p = ps.pkg;
- return p != null ? UserId.getUid(userId, p.applicationInfo.uid) : -1;
+ return p != null ? UserHandle.getUid(userId, p.applicationInfo.uid) : -1;
}
}
@@ -1961,7 +1961,7 @@
}
private void checkValidCaller(int uid, int userId) {
- if (UserId.getUserId(uid) == userId || uid == Process.SYSTEM_UID || uid == 0)
+ if (UserHandle.getUserId(uid) == userId || uid == Process.SYSTEM_UID || uid == 0)
return;
throw new SecurityException("Caller uid=" + uid
@@ -1990,7 +1990,7 @@
public int checkUidPermission(String permName, int uid) {
synchronized (mPackages) {
- Object obj = mSettings.getUserIdLPr(UserId.getAppId(uid));
+ Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
if (obj != null) {
GrantedPermissions gp = (GrantedPermissions)obj;
if (gp.grantedPermissions.contains(permName)) {
@@ -2024,7 +2024,7 @@
if (permName != null) {
BasePermission bp = findPermissionTreeLP(permName);
if (bp != null) {
- if (bp.uid == UserId.getAppId(Binder.getCallingUid())) {
+ if (bp.uid == UserHandle.getAppId(Binder.getCallingUid())) {
return bp;
}
throw new SecurityException("Calling uid "
@@ -2227,8 +2227,8 @@
public int checkUidSignatures(int uid1, int uid2) {
// Map to base uids.
- uid1 = UserId.getAppId(uid1);
- uid2 = UserId.getAppId(uid2);
+ uid1 = UserHandle.getAppId(uid1);
+ uid2 = UserHandle.getAppId(uid2);
// reader
synchronized (mPackages) {
Signature[] s1;
@@ -2286,7 +2286,7 @@
}
public String[] getPackagesForUid(int uid) {
- uid = UserId.getAppId(uid);
+ uid = UserHandle.getAppId(uid);
// reader
synchronized (mPackages) {
Object obj = mSettings.getUserIdLPr(uid);
@@ -2311,7 +2311,7 @@
public String getNameForUid(int uid) {
// reader
synchronized (mPackages) {
- Object obj = mSettings.getUserIdLPr(UserId.getAppId(uid));
+ Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
return sus.name + ":" + sus.userId;
@@ -2791,7 +2791,7 @@
final ParceledListSlice<PackageInfo> list = new ParceledListSlice<PackageInfo>();
final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;
final String[] keys;
- int userId = UserId.getCallingUserId();
+ int userId = UserHandle.getCallingUserId();
// writer
synchronized (mPackages) {
@@ -2890,7 +2890,7 @@
// reader
synchronized (mPackages) {
final Iterator<PackageParser.Package> i = mPackages.values().iterator();
- final int userId = UserId.getCallingUserId();
+ final int userId = UserHandle.getCallingUserId();
while (i.hasNext()) {
final PackageParser.Package p = i.next();
if (p.applicationInfo != null
@@ -2938,7 +2938,7 @@
synchronized (mPackages) {
final Iterator<Map.Entry<String, PackageParser.Provider>> i = mProviders.entrySet()
.iterator();
- final int userId = UserId.getCallingUserId();
+ final int userId = UserHandle.getCallingUserId();
while (i.hasNext()) {
Map.Entry<String, PackageParser.Provider> entry = i.next();
PackageParser.Provider p = entry.getValue();
@@ -2965,14 +2965,14 @@
synchronized (mPackages) {
final Iterator<PackageParser.Provider> i = mProvidersByComponent.values().iterator();
final int userId = processName != null ?
- UserId.getUserId(uid) : UserId.getCallingUserId();
+ UserHandle.getUserId(uid) : UserHandle.getCallingUserId();
while (i.hasNext()) {
final PackageParser.Provider p = i.next();
PackageSetting ps = mSettings.mPackages.get(p.owner.packageName);
if (p.info.authority != null
&& (processName == null
|| (p.info.processName.equals(processName)
- && UserId.isSameApp(p.info.applicationInfo.uid, uid)))
+ && UserHandle.isSameApp(p.info.applicationInfo.uid, uid)))
&& mSettings.isEnabledLPr(p.info, flags, userId)
&& (!mSafeMode
|| (p.info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)) {
@@ -3789,14 +3789,18 @@
boolean uidError = false;
if (dataPath.exists()) {
- // XXX should really do this check for each user.
- mOutPermissions[1] = 0;
- FileUtils.getPermissions(dataPath.getPath(), mOutPermissions);
+ int currentUid = 0;
+ try {
+ StructStat stat = Libcore.os.stat(dataPath.getPath());
+ currentUid = stat.st_uid;
+ } catch (ErrnoException e) {
+ Slog.e(TAG, "Couldn't stat path " + dataPath.getPath(), e);
+ }
// If we have mismatched owners for the data path, we have a problem.
- if (mOutPermissions[1] != pkg.applicationInfo.uid) {
+ if (currentUid != pkg.applicationInfo.uid) {
boolean recovered = false;
- if (mOutPermissions[1] == 0) {
+ if (currentUid == 0) {
// The directory somehow became owned by root. Wow.
// This is probably because the system was stopped while
// installd was in the middle of messing with its libs
@@ -3825,7 +3829,7 @@
? "System package " : "Third party package ";
String msg = prefix + pkg.packageName
+ " has changed from uid: "
- + mOutPermissions[1] + " to "
+ + currentUid + " to "
+ pkg.applicationInfo.uid + "; old data erased";
reportSettingsProblem(Log.WARN, msg);
recovered = true;
@@ -3857,11 +3861,11 @@
if (!recovered) {
pkg.applicationInfo.dataDir = "/mismatched_uid/settings_"
+ pkg.applicationInfo.uid + "/fs_"
- + mOutPermissions[1];
+ + currentUid;
pkg.applicationInfo.nativeLibraryDir = pkg.applicationInfo.dataDir;
String msg = "Package " + pkg.packageName
+ " has mismatched uid: "
- + mOutPermissions[1] + " on disk, "
+ + currentUid + " on disk, "
+ pkg.applicationInfo.uid + " in settings";
// writer
synchronized (mPackages) {
@@ -5148,7 +5152,7 @@
IActivityManager am = ActivityManagerNative.getDefault();
if (am != null) {
try {
- int[] userIds = userId == UserId.USER_ALL
+ int[] userIds = userId == UserHandle.USER_ALL
? sUserManager.getUserIds()
: new int[] {userId};
for (int id : userIds) {
@@ -5163,7 +5167,7 @@
// Modify the UID when posting to other users
int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
if (uid > 0 && id > 0) {
- uid = UserId.getUid(id, UserId.getAppId(uid));
+ uid = UserHandle.getUid(id, UserHandle.getAppId(uid));
intent.putExtra(Intent.EXTRA_UID, uid);
}
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
@@ -5312,13 +5316,13 @@
extras.putInt(Intent.EXTRA_UID, removedUid);
extras.putBoolean(Intent.EXTRA_DATA_REMOVED, false);
sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage,
- extras, null, null, UserId.USER_ALL);
+ extras, null, null, UserHandle.USER_ALL);
}
if (addedPackage != null) {
Bundle extras = new Bundle(1);
extras.putInt(Intent.EXTRA_UID, addedUid);
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, addedPackage,
- extras, null, null, UserId.USER_ALL);
+ extras, null, null, UserHandle.USER_ALL);
}
}
@@ -6484,6 +6488,10 @@
return false;
}
+ if (!SELinux.restorecon(newCodeFile)) {
+ return false;
+ }
+
return true;
}
}
@@ -7465,6 +7473,9 @@
FileUtils.setPermissions(
tmpPackageFile.getCanonicalPath(), FileUtils.S_IRUSR|FileUtils.S_IWUSR,
-1, -1);
+ if (!SELinux.restorecon(tmpPackageFile)) {
+ return null;
+ }
} catch (IOException e) {
Slog.e(TAG, "Trouble getting the canoncical path for a temp file.");
return null;
@@ -7539,11 +7550,11 @@
extras.putBoolean(Intent.EXTRA_REPLACING, true);
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, null, null, UserId.USER_ALL);
+ extras, null, null, UserHandle.USER_ALL);
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
- extras, null, null, UserId.USER_ALL);
+ extras, null, null, UserHandle.USER_ALL);
sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null,
- null, packageName, null, UserId.USER_ALL);
+ null, packageName, null, UserHandle.USER_ALL);
}
}
// Force a gc here.
@@ -7576,15 +7587,15 @@
}
if (removedPackage != null) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage,
- extras, null, null, UserId.USER_ALL);
+ extras, null, null, UserHandle.USER_ALL);
if (fullRemove && !replacing) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED, removedPackage,
- extras, null, null, UserId.USER_ALL);
+ extras, null, null, UserHandle.USER_ALL);
}
}
if (removedUid >= 0) {
sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras, null, null,
- UserId.getUserId(removedUid));
+ UserHandle.getUserId(removedUid));
}
}
}
@@ -7948,7 +7959,7 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DELETE_CACHE_FILES, null);
// Queue up an async operation since the package deletion may take a little while.
- final int userId = UserId.getCallingUserId();
+ final int userId = UserHandle.getCallingUserId();
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
@@ -8303,7 +8314,7 @@
+ "/" + className);
}
// Allow root and verify that userId is not being specified by a different user
- if (!allowedByPermission && !UserId.isSameApp(uid, pkgSetting.appId)) {
+ if (!allowedByPermission && !UserHandle.isSameApp(uid, pkgSetting.appId)) {
throw new SecurityException(
"Permission Denial: attempt to change component state from pid="
+ Binder.getCallingPid()
@@ -8352,7 +8363,7 @@
}
}
mSettings.writePackageRestrictionsLPr(userId);
- packageUid = UserId.getUid(userId, pkgSetting.appId);
+ packageUid = UserHandle.getUid(userId, pkgSetting.appId);
components = mPendingBroadcasts.get(packageName);
final boolean newPackage = components == null;
if (newPackage) {
@@ -8401,7 +8412,7 @@
extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, killFlag);
extras.putInt(Intent.EXTRA_UID, packageUid);
sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, null, null,
- UserId.getUserId(packageUid));
+ UserHandle.getUserId(packageUid));
}
public void setPackageStoppedState(String packageName, boolean stopped, int userId) {
@@ -9027,7 +9038,7 @@
}
String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
: Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
- sendPackageBroadcast(action, null, extras, null, finishedReceiver, UserId.USER_ALL);
+ sendPackageBroadcast(action, null, extras, null, finishedReceiver, UserHandle.USER_ALL);
}
}
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index add91d3..cfc0f5c 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -49,7 +49,7 @@
import android.os.FileUtils;
import android.os.Process;
import android.os.RemoteException;
-import android.os.UserId;
+import android.os.UserHandle;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
@@ -2353,7 +2353,7 @@
boolean setPackageStoppedStateLPw(String packageName, boolean stopped,
boolean allowedByPermission, int uid, int userId) {
- int appId = UserId.getAppId(uid);
+ int appId = UserHandle.getAppId(uid);
final PackageSetting pkgSetting = mPackages.get(packageName);
if (pkgSetting == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index b55dd24..a828864 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -22,6 +22,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
+import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -34,7 +35,8 @@
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
+import android.util.AtomicFile;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
@@ -55,22 +57,17 @@
public class UserManagerService extends IUserManager.Stub {
- private static final String TAG = "UserManagerService";
+ private static final String LOG_TAG = "UserManagerService";
private static final String TAG_NAME = "name";
-
private static final String ATTR_FLAGS = "flags";
-
private static final String ATTR_ICON_PATH = "icon";
-
private static final String ATTR_ID = "id";
-
+ private static final String ATTR_SERIAL_NO = "serialNumber";
+ private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber";
private static final String TAG_USERS = "users";
-
private static final String TAG_USER = "user";
- private static final String LOG_TAG = "UserManager";
-
private static final String USER_INFO_DIR = "system" + File.separator + "users";
private static final String USER_LIST_FILENAME = "userlist.xml";
private static final String USER_PHOTO_FILENAME = "photo.png";
@@ -81,6 +78,7 @@
private final File mUserListFile;
private int[] mUserIds;
private boolean mGuestEnabled;
+ private int mNextSerialNumber;
private Installer mInstaller;
private File mBaseUserPath;
@@ -125,7 +123,7 @@
@Override
public List<UserInfo> getUsers() {
- enforceSystemOrRoot("Only the system can query users");
+ checkManageUsersPermission("query users");
synchronized (mUsers) {
ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
for (int i = 0; i < mUsers.size(); i++) {
@@ -137,7 +135,7 @@
@Override
public UserInfo getUserInfo(int userId) {
- enforceSystemOrRoot("Only the system can query user");
+ checkManageUsersPermission("query user");
synchronized (mUsers) {
UserInfo info = mUsers.get(userId);
return info;
@@ -152,7 +150,7 @@
@Override
public void setUserName(int userId, String name) {
- enforceSystemOrRoot("Only the system can rename users");
+ checkManageUsersPermission("rename users");
synchronized (mUsers) {
UserInfo info = mUsers.get(userId);
if (name != null && !name.equals(info.name)) {
@@ -164,7 +162,7 @@
@Override
public ParcelFileDescriptor setUserIcon(int userId) {
- enforceSystemOrRoot("Only the system can update users");
+ checkManageUsersPermission("update users");
synchronized (mUsers) {
UserInfo info = mUsers.get(userId);
if (info == null) return null;
@@ -178,7 +176,7 @@
@Override
public void setGuestEnabled(boolean enable) {
- enforceSystemOrRoot("Only the system can enable guest users");
+ checkManageUsersPermission("enable guest users");
synchronized (mUsers) {
if (mGuestEnabled != enable) {
mGuestEnabled = enable;
@@ -209,7 +207,7 @@
@Override
public void wipeUser(int userHandle) {
- enforceSystemOrRoot("Only the system can wipe users");
+ checkManageUsersPermission("wipe user");
// TODO:
}
@@ -220,10 +218,13 @@
* @param message used as message if SecurityException is thrown
* @throws SecurityException if the caller is not system or root
*/
- private static final void enforceSystemOrRoot(String message) {
+ private static final void checkManageUsersPermission(String message) {
final int uid = Binder.getCallingUid();
- if (uid != Process.SYSTEM_UID && uid != 0) {
- throw new SecurityException(message);
+ if (uid != Process.SYSTEM_UID && uid != 0
+ && ActivityManager.checkComponentPermission(
+ android.Manifest.permission.MANAGE_USERS,
+ uid, -1, true) != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("You need MANAGE_USERS permission to: " + message);
}
}
@@ -243,7 +244,7 @@
info.iconPath = file.getAbsolutePath();
return fd;
} catch (FileNotFoundException e) {
- Slog.w(TAG, "Error setting photo for user ", e);
+ Slog.w(LOG_TAG, "Error setting photo for user ", e);
}
return null;
}
@@ -270,8 +271,9 @@
return;
}
FileInputStream fis = null;
+ AtomicFile userListFile = new AtomicFile(mUserListFile);
try {
- fis = new FileInputStream(mUserListFile);
+ fis = userListFile.openRead();
XmlPullParser parser = Xml.newPullParser();
parser.setInput(fis, null);
int type;
@@ -286,15 +288,26 @@
return;
}
+ mNextSerialNumber = -1;
+ if (parser.getName().equals(TAG_USERS)) {
+ String lastSerialNumber = parser.getAttributeValue(null, ATTR_NEXT_SERIAL_NO);
+ if (lastSerialNumber != null) {
+ mNextSerialNumber = Integer.parseInt(lastSerialNumber);
+ }
+ }
+
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
String id = parser.getAttributeValue(null, ATTR_ID);
UserInfo user = readUser(Integer.parseInt(id));
if (user != null) {
mUsers.put(user.id, user);
- }
- if (user.isGuest()) {
- mGuestEnabled = true;
+ if (user.isGuest()) {
+ mGuestEnabled = true;
+ }
+ if (mNextSerialNumber < 0 || mNextSerialNumber <= user.id) {
+ mNextSerialNumber = user.id + 1;
+ }
}
}
}
@@ -333,9 +346,9 @@
*/
private void writeUserLocked(UserInfo userInfo) {
FileOutputStream fos = null;
+ AtomicFile userFile = new AtomicFile(new File(mUsersDir, userInfo.id + ".xml"));
try {
- final File mUserFile = new File(mUsersDir, userInfo.id + ".xml");
- fos = new FileOutputStream(mUserFile);
+ fos = userFile.startWrite();
final BufferedOutputStream bos = new BufferedOutputStream(fos);
// XmlSerializer serializer = XmlUtils.serializerInstance();
@@ -346,6 +359,7 @@
serializer.startTag(null, TAG_USER);
serializer.attribute(null, ATTR_ID, Integer.toString(userInfo.id));
+ serializer.attribute(null, ATTR_SERIAL_NO, Integer.toString(userInfo.serialNumber));
serializer.attribute(null, ATTR_FLAGS, Integer.toString(userInfo.flags));
if (userInfo.iconPath != null) {
serializer.attribute(null, ATTR_ICON_PATH, userInfo.iconPath);
@@ -358,30 +372,26 @@
serializer.endTag(null, TAG_USER);
serializer.endDocument();
- } catch (IOException ioe) {
+ userFile.finishWrite(fos);
+ } catch (Exception ioe) {
Slog.e(LOG_TAG, "Error writing user info " + userInfo.id + "\n" + ioe);
- } finally {
- if (fos != null) {
- try {
- fos.close();
- } catch (IOException ioe) {
- }
- }
+ userFile.failWrite(fos);
}
}
/*
* Writes the user list file in this format:
*
- * <users>
+ * <users nextSerialNumber="3">
* <user id="0"></user>
* <user id="2"></user>
* </users>
*/
private void writeUserListLocked() {
FileOutputStream fos = null;
+ AtomicFile userListFile = new AtomicFile(mUserListFile);
try {
- fos = new FileOutputStream(mUserListFile);
+ fos = userListFile.startWrite();
final BufferedOutputStream bos = new BufferedOutputStream(fos);
// XmlSerializer serializer = XmlUtils.serializerInstance();
@@ -391,6 +401,7 @@
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
serializer.startTag(null, TAG_USERS);
+ serializer.attribute(null, ATTR_NEXT_SERIAL_NO, Integer.toString(mNextSerialNumber));
for (int i = 0; i < mUsers.size(); i++) {
UserInfo user = mUsers.valueAt(i);
@@ -402,27 +413,24 @@
serializer.endTag(null, TAG_USERS);
serializer.endDocument();
- } catch (IOException ioe) {
+ userListFile.finishWrite(fos);
+ } catch (Exception e) {
+ userListFile.failWrite(fos);
Slog.e(LOG_TAG, "Error writing user list");
- } finally {
- if (fos != null) {
- try {
- fos.close();
- } catch (IOException ioe) {
- }
- }
}
}
private UserInfo readUser(int id) {
int flags = 0;
+ int serialNumber = id;
String name = null;
String iconPath = null;
FileInputStream fis = null;
try {
- File userFile = new File(mUsersDir, Integer.toString(id) + ".xml");
- fis = new FileInputStream(userFile);
+ AtomicFile userFile =
+ new AtomicFile(new File(mUsersDir, Integer.toString(id) + ".xml"));
+ fis = userFile.openRead();
XmlPullParser parser = Xml.newPullParser();
parser.setInput(fis, null);
int type;
@@ -442,6 +450,10 @@
Slog.e(LOG_TAG, "User id does not match the file name");
return null;
}
+ String serialNumberValue = parser.getAttributeValue(null, ATTR_SERIAL_NO);
+ if (serialNumberValue != null) {
+ serialNumber = Integer.parseInt(serialNumberValue);
+ }
String flagString = parser.getAttributeValue(null, ATTR_FLAGS);
flags = Integer.parseInt(flagString);
iconPath = parser.getAttributeValue(null, ATTR_ICON_PATH);
@@ -458,6 +470,7 @@
}
UserInfo userInfo = new UserInfo(id, name, iconPath, flags);
+ userInfo.serialNumber = serialNumber;
return userInfo;
} catch (IOException ioe) {
@@ -475,7 +488,7 @@
@Override
public UserInfo createUser(String name, int flags) {
- enforceSystemOrRoot("Only the system can create users");
+ checkManageUsersPermission("Only the system can create users");
int userId = getNextAvailableId();
UserInfo userInfo = new UserInfo(userId, name, null, flags);
File userPath = new File(mBaseUserPath, Integer.toString(userId));
@@ -483,6 +496,7 @@
return null;
}
synchronized (mUsers) {
+ userInfo.serialNumber = mNextSerialNumber++;
mUsers.put(userId, userInfo);
writeUserListLocked();
writeUserLocked(userInfo);
@@ -490,8 +504,8 @@
}
if (userInfo != null) {
Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
- addedIntent.putExtra(Intent.EXTRA_USERID, userInfo.id);
- mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_ACCOUNTS);
+ addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id);
+ mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS);
}
return userInfo;
}
@@ -502,9 +516,34 @@
* @param id the user's id
*/
public boolean removeUser(int userHandle) {
- enforceSystemOrRoot("Only the system can remove users");
+ checkManageUsersPermission("Only the system can remove users");
+ boolean result;
synchronized (mUsers) {
- return removeUserLocked(userHandle);
+ result = removeUserLocked(userHandle);
+ }
+ // Let other services shutdown any activity
+ Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED);
+ addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle);
+ mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS);
+ return result;
+ }
+
+ @Override
+ public int getUserSerialNumber(int userHandle) {
+ synchronized (mUsers) {
+ if (!exists(userHandle)) return -1;
+ return getUserInfo(userHandle).serialNumber;
+ }
+ }
+
+ @Override
+ public int getUserHandle(int userSerialNumber) {
+ synchronized (mUsers) {
+ for (int userId : mUserIds) {
+ if (getUserInfo(userId).serialNumber == userSerialNumber) return userId;
+ }
+ // Not found
+ return -1;
}
}
@@ -519,17 +558,12 @@
// Remove this user from the list
mUsers.remove(userHandle);
// Remove user file
- File userFile = new File(mUsersDir, userHandle + ".xml");
+ AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + ".xml"));
userFile.delete();
// Update the user list
writeUserListLocked();
updateUserIdsLocked();
- // Let other services shutdown any activity
- Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED);
- addedIntent.putExtra(Intent.EXTRA_USERID, userHandle);
- mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_ACCOUNTS);
-
removePackageFolders(userHandle);
return true;
}
@@ -539,7 +573,7 @@
// Don't do it for the primary user, it will become recursive.
if (userId == 0)
continue;
- mInstaller.createUserData(packageName, UserId.getUid(userId, uid),
+ mInstaller.createUserData(packageName, UserHandle.getUid(userId, uid),
userId);
}
}
diff --git a/services/java/com/android/server/power/DisplayPowerController.java b/services/java/com/android/server/power/DisplayPowerController.java
index 50d3f81..f3183f8 100644
--- a/services/java/com/android/server/power/DisplayPowerController.java
+++ b/services/java/com/android/server/power/DisplayPowerController.java
@@ -33,11 +33,11 @@
import android.os.Message;
import android.os.SystemClock;
import android.util.Slog;
+import android.util.Spline;
import android.util.TimeUtils;
import java.io.PrintWriter;
import java.io.StringWriter;
-import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
@@ -70,6 +70,12 @@
private static final boolean DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT = false;
private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false;
+ // If true, uses the electron beam on animation.
+ // We might want to turn this off if we cannot get a guarantee that the screen
+ // actually turns on and starts showing new content after the call to set the
+ // screen state returns.
+ private static final boolean USE_ELECTRON_BEAM_ON_ANIMATION = true;
+
private static final int ELECTRON_BEAM_ON_ANIMATION_DURATION_MILLIS = 300;
private static final int ELECTRON_BEAM_OFF_ANIMATION_DURATION_MILLIS = 600;
@@ -96,9 +102,20 @@
// Filter time constant in milliseconds for computing a moving
// average of light samples. Different constants are used
- // to adapt to brighter or dimmer environments.
- private static final long BRIGHTENING_LIGHT_TIME_CONSTANT = 2500; // 2.5 sec
- private static final long DIMMING_LIGHT_TIME_CONSTANT = 10000; // 10 sec
+ // to calculate the average light level when adapting to brighter or
+ // dimmer environments.
+ // This parameter only controls the filtering of light samples.
+ private static final long BRIGHTENING_LIGHT_TIME_CONSTANT = 500;
+ private static final long DIMMING_LIGHT_TIME_CONSTANT = 2000;
+
+ // Stability requirements in milliseconds for accepting a new brightness
+ // level. This is used for debouncing the light sensor. Different constants
+ // are used to debounce the light sensor when adapting to brighter or dimmer
+ // environments.
+ // This parameter controls how quickly brightness changes occur in response to
+ // an observed change in light level.
+ private static final long BRIGHTENING_LIGHT_DEBOUNCE = 2500;
+ private static final long DIMMING_LIGHT_DEBOUNCE = 10000;
private final Object mLock = new Object();
@@ -133,8 +150,7 @@
// Auto-brightness.
private boolean mUseSoftwareAutoBrightnessConfig;
- private int[] mAutoBrightnessLevelsConfig;
- private int[] mAutoBrightnessLcdBacklightValuesConfig;
+ private Spline mScreenAutoBrightnessSpline;
// Amount of time to delay auto-brightness after screen on while waiting for
// the light sensor to warm-up in milliseconds.
@@ -233,6 +249,9 @@
// The time of the most light recent sample.
private long mLastLightSampleTime;
+ // The time when we accumulated the first recent light sample into mRecentLightSamples.
+ private long mFirstRecentLightSampleTime;
+
// The upcoming debounce light sensor time.
// This is only valid when mLightMeasurementValue && mRecentLightSamples >= 1.
private long mPendingLightSensorDebounceTime;
@@ -275,17 +294,18 @@
mUseSoftwareAutoBrightnessConfig = resources.getBoolean(
com.android.internal.R.bool.config_automatic_brightness_available);
if (mUseSoftwareAutoBrightnessConfig) {
- mAutoBrightnessLevelsConfig = resources.getIntArray(
+ int[] lux = resources.getIntArray(
com.android.internal.R.array.config_autoBrightnessLevels);
- mAutoBrightnessLcdBacklightValuesConfig = resources.getIntArray(
+ int[] screenBrightness = resources.getIntArray(
com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
- if (mAutoBrightnessLcdBacklightValuesConfig.length
- != mAutoBrightnessLevelsConfig.length + 1) {
+
+ mScreenAutoBrightnessSpline = createAutoBrightnessSpline(lux, screenBrightness);
+ if (mScreenAutoBrightnessSpline == null) {
Slog.e(TAG, "Error in config.xml. config_autoBrightnessLcdBacklightValues "
- + "(size " + mAutoBrightnessLcdBacklightValuesConfig.length + ") "
- + "should have exactly one more entry than "
- + "config_autoBrightnessLevels (size "
- + mAutoBrightnessLevelsConfig.length + "). "
+ + "(size " + screenBrightness.length + ") "
+ + "must be monotic and have exactly one more entry than "
+ + "config_autoBrightnessLevels (size " + lux.length + ") "
+ + "which must be strictly increasing. "
+ "Auto-brightness will be disabled.");
mUseSoftwareAutoBrightnessConfig = false;
}
@@ -308,6 +328,31 @@
}
}
+ private static Spline createAutoBrightnessSpline(int[] lux, int[] brightness) {
+ try {
+ final int n = brightness.length;
+ float[] x = new float[n];
+ float[] y = new float[n];
+ y[0] = brightness[0];
+ for (int i = 1; i < n; i++) {
+ x[i] = lux[i - 1];
+ y[i] = brightness[i];
+ }
+
+ Spline spline = Spline.createMonotoneCubicSpline(x, y);
+ if (false) {
+ Slog.d(TAG, "Auto-brightness spline: " + spline);
+ for (float v = 1f; v < lux[lux.length - 1] * 1.25f; v *= 1.25f) {
+ Slog.d(TAG, String.format(" %7.1f: %7.1f", v, spline.interpolate(v)));
+ }
+ }
+ return spline;
+ } catch (IllegalArgumentException ex) {
+ Slog.e(TAG, "Could not create auto-brightness spline.", ex);
+ return null;
+ }
+ }
+
/**
* Returns true if the proximity sensor screen-off function is available.
*/
@@ -511,14 +556,19 @@
// on animation immediately then the results are pretty ugly.
if (!mElectronBeamOffAnimator.isStarted()) {
setScreenOn(true);
- if (!mElectronBeamOnAnimator.isStarted()) {
- if (mPowerState.getElectronBeamLevel() == 1.0f) {
- mPowerState.dismissElectronBeam();
- } else if (mPowerState.prepareElectronBeam(true)) {
- mElectronBeamOnAnimator.start();
- } else {
- mElectronBeamOnAnimator.end();
+ if (USE_ELECTRON_BEAM_ON_ANIMATION) {
+ if (!mElectronBeamOnAnimator.isStarted()) {
+ if (mPowerState.getElectronBeamLevel() == 1.0f) {
+ mPowerState.dismissElectronBeam();
+ } else if (mPowerState.prepareElectronBeam(true)) {
+ mElectronBeamOnAnimator.start();
+ } else {
+ mElectronBeamOnAnimator.end();
+ }
}
+ } else {
+ mPowerState.setElectronBeamLevel(1.0f);
+ mPowerState.dismissElectronBeam();
}
}
} else {
@@ -664,7 +714,6 @@
// If the newest light sample doesn't seem to be going in the
// same general direction as recent samples, then start over.
setRecentLight(time, lux, lux > mLightMeasurement);
- mPendingLightSensorDebounceTime = time + mRecentLightTimeConstant;
} else if (mRecentLightSamples >= 1) {
// Add the newest light sample to the moving average.
accumulateRecentLight(time, lux);
@@ -677,6 +726,8 @@
+ ", mRecentLightAverage=" + mRecentLightAverage
+ ", mRecentLightBrightening=" + mRecentLightBrightening
+ ", mRecentLightTimeConstant=" + mRecentLightTimeConstant
+ + ", mFirstRecentLightSampleTime="
+ + TimeUtils.formatUptime(mFirstRecentLightSampleTime)
+ ", mPendingLightSensorDebounceTime="
+ TimeUtils.formatUptime(mPendingLightSensorDebounceTime));
}
@@ -694,6 +745,9 @@
mRecentLightAverage = lux;
mLastLightSample = lux;
mLastLightSampleTime = time;
+ mFirstRecentLightSampleTime = time;
+ mPendingLightSensorDebounceTime = time + (brightening ?
+ BRIGHTENING_LIGHT_DEBOUNCE : DIMMING_LIGHT_DEBOUNCE);
}
private void accumulateRecentLight(long time, float lux) {
@@ -715,8 +769,7 @@
if (DEBUG) {
Slog.d(TAG, "debounceLightSensor: Accepted new measurement "
+ mLightMeasurement + " after "
- + (now - mPendingLightSensorDebounceTime
- + mRecentLightTimeConstant) + " ms based on "
+ + (now - mFirstRecentLightSampleTime) + " ms based on "
+ mRecentLightSamples + " recent samples.");
}
@@ -751,13 +804,13 @@
return;
}
- final int newScreenAutoBrightness = mapLuxToBrightness(mLightMeasurement,
- mAutoBrightnessLevelsConfig,
- mAutoBrightnessLcdBacklightValuesConfig);
+ final int newScreenAutoBrightness = interpolateBrightness(
+ mScreenAutoBrightnessSpline, mLightMeasurement);
if (mScreenAutoBrightness != newScreenAutoBrightness) {
if (DEBUG) {
Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness="
- + mScreenAutoBrightness);
+ + mScreenAutoBrightness + "newScreenAutoBrightness="
+ + newScreenAutoBrightness);
}
mScreenAutoBrightness = newScreenAutoBrightness;
@@ -767,20 +820,8 @@
}
}
- /**
- * Maps a light sensor measurement in lux to a brightness value given
- * a table of lux breakpoint values and a table of brightnesses that
- * is one element larger.
- */
- private static int mapLuxToBrightness(float lux,
- int[] fromLux, int[] toBrightness) {
- // TODO implement interpolation and possibly range expansion
- int level = 0;
- final int count = fromLux.length;
- while (level < count && lux >= fromLux[level]) {
- level += 1;
- }
- return toBrightness[level];
+ private static int interpolateBrightness(Spline spline, float lux) {
+ return Math.min(255, Math.max(0, (int)Math.round(spline.interpolate(lux))));
}
private void sendOnStateChanged() {
@@ -822,10 +863,7 @@
pw.println(" mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig);
pw.println(" mUseSoftwareAutoBrightnessConfig="
+ mUseSoftwareAutoBrightnessConfig);
- pw.println(" mAutoBrightnessLevelsConfig="
- + Arrays.toString(mAutoBrightnessLevelsConfig));
- pw.println(" mAutoBrightnessLcdBacklightValuesConfig="
- + Arrays.toString(mAutoBrightnessLcdBacklightValuesConfig));
+ pw.println(" mScreenAutoBrightnessSpline=" + mScreenAutoBrightnessSpline);
pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
if (Looper.myLooper() == mHandler.getLooper()) {
@@ -885,6 +923,8 @@
pw.println(" mRecentLightAverage=" + mRecentLightAverage);
pw.println(" mRecentLightBrightening=" + mRecentLightBrightening);
pw.println(" mRecentLightTimeConstant=" + mRecentLightTimeConstant);
+ pw.println(" mFirstRecentLightSampleTime="
+ + TimeUtils.formatUptime(mFirstRecentLightSampleTime));
pw.println(" mPendingLightSensorDebounceTime="
+ TimeUtils.formatUptime(mPendingLightSensorDebounceTime));
pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness);
diff --git a/services/java/com/android/server/power/DisplayPowerState.java b/services/java/com/android/server/power/DisplayPowerState.java
index ad242c0..64a0462 100644
--- a/services/java/com/android/server/power/DisplayPowerState.java
+++ b/services/java/com/android/server/power/DisplayPowerState.java
@@ -242,8 +242,9 @@
mElectronBeam.draw(mElectronBeamLevel);
}
- if ((mDirty & DIRTY_BRIGHTNESS) != 0) {
- mScreenBrightnessModulator.setBrightness(mScreenBrightness);
+ if ((mDirty & (DIRTY_BRIGHTNESS | DIRTY_SCREEN_ON | DIRTY_ELECTRON_BEAM)) != 0) {
+ mScreenBrightnessModulator.setBrightness(mScreenOn ?
+ (int)(mScreenBrightness * mElectronBeamLevel) : 0);
}
if ((mDirty & DIRTY_SCREEN_ON) != 0 && mScreenOn) {
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 4cf46a0..c1884da 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -5912,7 +5912,7 @@
}
}
- rebuildBlackFrame();
+ rebuildBlackFrameLocked();
final WindowList windows = displayContent.getWindowList();
for (int i = windows.size() - 1; i >= 0; i--) {
@@ -6929,6 +6929,8 @@
displayReady(Display.DEFAULT_DISPLAY);
synchronized(mWindowMap) {
+ readForcedDisplaySizeAndDensityLocked(getDefaultDisplayContent());
+
WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
mDisplay = wm.getDefaultDisplay();
mIsTouchDevice = mContext.getPackageManager().hasSystemFeature(
@@ -6950,6 +6952,11 @@
mPolicy.setInitialDisplaySize(mDisplay, displayContent.mInitialDisplayWidth,
displayContent.mInitialDisplayHeight, displayContent.mInitialDisplayDensity);
}
+
+ try {
+ mActivityManager.updateConfiguration(null);
+ } catch (RemoteException e) {
+ }
}
public void displayReady(int displayId) {
@@ -6968,15 +6975,6 @@
displayContent.mBaseDisplayDensity = displayContent.mInitialDisplayDensity;
}
}
-
- try {
- mActivityManager.updateConfiguration(null);
- } catch (RemoteException e) {
- }
-
- synchronized (mWindowMap) {
- readForcedDisplaySizeAndDensityLocked(getDisplayContent(displayId));
- }
}
public void systemReady() {
@@ -7605,7 +7603,7 @@
}
}
- private void rebuildBlackFrame() {
+ private void rebuildBlackFrameLocked() {
if (mBlackFrame != null) {
mBlackFrame.kill();
mBlackFrame = null;
@@ -7617,6 +7615,13 @@
int initW, initH, baseW, baseH;
final boolean rotated = (mRotation == Surface.ROTATION_90
|| mRotation == Surface.ROTATION_270);
+ if (DEBUG_BOOT) {
+ Slog.i(TAG, "BLACK FRAME: rotated=" + rotated + " init="
+ + displayContent.mInitialDisplayWidth + "x"
+ + displayContent.mInitialDisplayHeight + " base="
+ + displayContent.mBaseDisplayWidth + "x"
+ + displayContent.mBaseDisplayHeight);
+ }
if (rotated) {
initW = displayContent.mInitialDisplayHeight;
initH = displayContent.mInitialDisplayWidth;
@@ -7678,7 +7683,7 @@
}
}
if (changed) {
- reconfigureDisplayLocked(displayContent);
+ rebuildBlackFrameLocked();
}
}
@@ -7707,7 +7712,7 @@
final DisplayContent displayContent = getDisplayContent(displayId);
setForcedDisplayDensityLocked(displayContent, density);
Settings.Secure.putString(mContext.getContentResolver(),
- Settings.Secure.DISPLAY_SIZE_FORCED, Integer.toString(density));
+ Settings.Secure.DISPLAY_DENSITY_FORCED, Integer.toString(density));
}
}
@@ -7750,7 +7755,7 @@
mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
}
- rebuildBlackFrame();
+ rebuildBlackFrameLocked();
performLayoutAndPlaceSurfacesLocked();
}
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 3f66de6..89b2474 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -33,6 +33,7 @@
<uses-permission android:name="android.permission.MODIFY_NETWORK_ACCOUNTING" />
<uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+ <uses-permission android:name="android.permission.MANAGE_USERS" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index f6f9aa0..3373fd4 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -71,7 +71,7 @@
import android.os.INetworkManagementService;
import android.os.IPowerManager;
import android.os.MessageQueue.IdleHandler;
-import android.os.UserId;
+import android.os.UserHandle;
import android.test.AndroidTestCase;
import android.test.mock.MockPackageManager;
import android.test.suitebuilder.annotation.LargeTest;
@@ -138,10 +138,10 @@
private static final int APP_ID_A = android.os.Process.FIRST_APPLICATION_UID + 800;
private static final int APP_ID_B = android.os.Process.FIRST_APPLICATION_UID + 801;
- private static final int UID_A = UserId.getUid(USER_ID, APP_ID_A);
- private static final int UID_B = UserId.getUid(USER_ID, APP_ID_B);
- private static final int UID_A_GUEST = UserId.getUid(USER_ID_GUEST, APP_ID_A);
- private static final int UID_B_GUEST = UserId.getUid(USER_ID_GUEST, APP_ID_B);
+ private static final int UID_A = UserHandle.getUid(USER_ID, APP_ID_A);
+ private static final int UID_B = UserHandle.getUid(USER_ID, APP_ID_B);
+ private static final int UID_A_GUEST = UserHandle.getUid(USER_ID_GUEST, APP_ID_A);
+ private static final int UID_B_GUEST = UserHandle.getUid(USER_ID_GUEST, APP_ID_B);
private static final int PID_1 = 400;
private static final int PID_2 = 401;
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index bc3649c..59a86c2 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -16,35 +16,23 @@
package com.android.server.pm;
-import com.android.server.pm.UserManagerService;
-
+import android.content.Context;
import android.content.pm.UserInfo;
import android.os.Debug;
import android.os.Environment;
+import android.os.UserManager;
import android.test.AndroidTestCase;
import java.util.List;
-/** Test {@link UserManagerService} functionality. */
+/** Test {@link UserManager} functionality. */
public class UserManagerTest extends AndroidTestCase {
- UserManagerService mUserManager = null;
+ UserManager mUserManager = null;
@Override
public void setUp() throws Exception {
- mUserManager = new UserManagerService(Environment.getExternalStorageDirectory(),
- Environment.getExternalStorageDirectory());
- }
-
- @Override
- public void tearDown() throws Exception {
- List<UserInfo> users = mUserManager.getUsers();
- // Remove all except the primary user
- for (UserInfo user : users) {
- if (!user.isPrimary()) {
- mUserManager.removeUser(user.id);
- }
- }
+ mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
}
public void testHasPrimary() throws Exception {
@@ -52,12 +40,10 @@
}
public void testAddUser() throws Exception {
- final UserManagerService details = mUserManager;
-
- UserInfo userInfo = details.createUser("Guest 1", UserInfo.FLAG_GUEST);
+ UserInfo userInfo = mUserManager.createUser("Guest 1", UserInfo.FLAG_GUEST);
assertTrue(userInfo != null);
- List<UserInfo> list = details.getUsers();
+ List<UserInfo> list = mUserManager.getUsers();
boolean found = false;
for (UserInfo user : list) {
if (user.id == userInfo.id && user.name.equals("Guest 1")
@@ -68,13 +54,12 @@
}
}
assertTrue(found);
+ mUserManager.removeUser(userInfo.id);
}
public void testAdd2Users() throws Exception {
- final UserManagerService details = mUserManager;
-
- UserInfo user1 = details.createUser("Guest 1", UserInfo.FLAG_GUEST);
- UserInfo user2 = details.createUser("User 2", UserInfo.FLAG_ADMIN);
+ UserInfo user1 = mUserManager.createUser("Guest 1", UserInfo.FLAG_GUEST);
+ UserInfo user2 = mUserManager.createUser("User 2", UserInfo.FLAG_ADMIN);
assertTrue(user1 != null);
assertTrue(user2 != null);
@@ -82,14 +67,14 @@
assertTrue(findUser(0));
assertTrue(findUser(user1.id));
assertTrue(findUser(user2.id));
+ mUserManager.removeUser(user1.id);
+ mUserManager.removeUser(user2.id);
}
public void testRemoveUser() throws Exception {
- final UserManagerService details = mUserManager;
+ UserInfo userInfo = mUserManager.createUser("Guest 1", UserInfo.FLAG_GUEST);
- UserInfo userInfo = details.createUser("Guest 1", UserInfo.FLAG_GUEST);
-
- details.removeUser(userInfo.id);
+ mUserManager.removeUser(userInfo.id);
assertFalse(findUser(userInfo.id));
}
@@ -104,4 +89,18 @@
}
return false;
}
+
+ public void testSerialNumber() {
+ UserInfo user1 = mUserManager.createUser("User 1", UserInfo.FLAG_RESTRICTED);
+ int serialNumber1 = user1.serialNumber;
+ assertEquals(serialNumber1, mUserManager.getUserSerialNumber(user1.id));
+ assertEquals(user1.id, mUserManager.getUserHandle(serialNumber1));
+ mUserManager.removeUser(user1.id);
+ UserInfo user2 = mUserManager.createUser("User 2", UserInfo.FLAG_RESTRICTED);
+ int serialNumber2 = user2.serialNumber;
+ assertFalse(serialNumber1 == serialNumber2);
+ assertEquals(serialNumber2, mUserManager.getUserSerialNumber(user2.id));
+ assertEquals(user2.id, mUserManager.getUserHandle(serialNumber2));
+ mUserManager.removeUser(user2.id);
+ }
}
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index 36f2c14..02d0b45 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -38,6 +38,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
+import android.os.UserHandle;
import java.io.File;
import java.io.FileInputStream;
@@ -304,12 +305,12 @@
}
@Override
- public void sendBroadcastToUser(Intent intent, int userId) {
+ public void sendBroadcastAsUser(Intent intent, UserHandle user) {
throw new UnsupportedOperationException();
}
@Override
- public void sendOrderedBroadcastToUser(Intent intent, int userId,
+ public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
BroadcastReceiver resultReceiver, Handler scheduler,
int initialCode, String initialData, Bundle initialExtras) {
throw new UnsupportedOperationException();
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
index 0577dbb..9599b19 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
@@ -30,6 +30,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.graphics.Bitmap;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -187,7 +188,7 @@
menu.add("Send to user 1!").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override public boolean onMenuItemClick(MenuItem item) {
Intent intent = new Intent(ActivityTestMain.this, UserTarget.class);
- sendOrderedBroadcastToUser(intent, 1, new BroadcastResultReceiver(),
+ sendOrderedBroadcastAsUser(intent, new UserHandle(1), new BroadcastResultReceiver(),
null, Activity.RESULT_OK, null, null);
return true;
}
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/SingleUserProvider.java b/tests/ActivityTests/src/com/google/android/test/activity/SingleUserProvider.java
index 83785e4..1e7b8f4 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/SingleUserProvider.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/SingleUserProvider.java
@@ -21,7 +21,7 @@
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Process;
+import android.os.UserHandle;
public class SingleUserProvider extends ContentProvider {
static final String AUTHORITY = "com.google.android.test.activity.single_user";
@@ -60,7 +60,7 @@
@Override
public Bundle call(String method, String arg, Bundle extras) {
Bundle res = new Bundle();
- res.putInt("user", Process.myUserHandle());
+ res.putInt("user", UserHandle.myUserId());
return res;
}
}
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/SingleUserReceiver.java b/tests/ActivityTests/src/com/google/android/test/activity/SingleUserReceiver.java
index 9295cf48..8afa478 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/SingleUserReceiver.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/SingleUserReceiver.java
@@ -21,12 +21,13 @@
import android.content.Intent;
import android.os.Bundle;
import android.os.Process;
+import android.os.UserHandle;
public class SingleUserReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Bundle res = getResultExtras(true);
- res.putInt("user", Process.myUserHandle());
+ res.putInt("user", UserHandle.myUserId());
setResultExtras(res);
}
}
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/UserTarget.java b/tests/ActivityTests/src/com/google/android/test/activity/UserTarget.java
index 9c6a9f1..47539a1 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/UserTarget.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/UserTarget.java
@@ -21,6 +21,7 @@
import android.content.Intent;
import android.os.Bundle;
import android.os.Process;
+import android.os.UserHandle;
import android.util.Log;
public class UserTarget extends BroadcastReceiver {
@@ -28,7 +29,7 @@
public void onReceive(Context context, Intent intent) {
Log.i("ActivityTest", "Received: " + intent);
Bundle res = getResultExtras(true);
- res.putInt("user", Process.myUserHandle());
+ res.putInt("user", UserHandle.myUserId());
setResultExtras(res);
}
}
diff --git a/tests/RenderScriptTests/ComputeBenchmark/Android.mk b/tests/RenderScriptTests/ComputeBenchmark/Android.mk
new file mode 100644
index 0000000..8d47e89
--- /dev/null
+++ b/tests/RenderScriptTests/ComputeBenchmark/Android.mk
@@ -0,0 +1,27 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+ $(call all-renderscript-files-under, src)
+
+LOCAL_PACKAGE_NAME := RsComputeBenchmark
+
+include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/ComputeBenchmark/AndroidManifest.xml b/tests/RenderScriptTests/ComputeBenchmark/AndroidManifest.xml
new file mode 100644
index 0000000..c8fcc17
--- /dev/null
+++ b/tests/RenderScriptTests/ComputeBenchmark/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.rs.computebench">
+
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-sdk android:minSdkVersion="17" />
+ <application android:label="_RS_Compute_Bench">
+ <activity android:name="ComputeBench">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/RenderScriptTests/ComputeBenchmark/res/layout/main.xml b/tests/RenderScriptTests/ComputeBenchmark/res/layout/main.xml
new file mode 100644
index 0000000..9e9dab8
--- /dev/null
+++ b/tests/RenderScriptTests/ComputeBenchmark/res/layout/main.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ImageView
+ android:id="@+id/displayin"
+ android:layout_width="320dip"
+ android:layout_height="266dip" />
+
+ <ImageView
+ android:id="@+id/displayout"
+ android:layout_width="320dip"
+ android:layout_height="266dip" />
+
+</LinearLayout>
diff --git a/tests/RenderScriptTests/ComputeBenchmark/src/com/example/android/rs/computebench/Benchmark.java b/tests/RenderScriptTests/ComputeBenchmark/src/com/example/android/rs/computebench/Benchmark.java
new file mode 100644
index 0000000..ec80719
--- /dev/null
+++ b/tests/RenderScriptTests/ComputeBenchmark/src/com/example/android/rs/computebench/Benchmark.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.computebench;
+import android.content.Context;
+import android.content.res.Resources;
+import android.renderscript.*;
+
+public class Benchmark implements Runnable {
+ private final RenderScript mRS;
+ private ScriptC_compute_benchmark mScript;
+
+ public Benchmark(RenderScript rs, Resources res) {
+ mRS = rs;
+ mScript = new ScriptC_compute_benchmark(mRS, res, R.raw.compute_benchmark);
+ }
+
+ public void run() {
+ long t = java.lang.System.currentTimeMillis();
+ mScript.invoke_bench();
+ mRS.finish();
+ t = java.lang.System.currentTimeMillis() - t;
+ android.util.Log.v("ComputeBench", "Total benchmark took " + t + " ms");
+ }
+
+}
diff --git a/tests/RenderScriptTests/ComputeBenchmark/src/com/example/android/rs/computebench/ComputeBench.java b/tests/RenderScriptTests/ComputeBenchmark/src/com/example/android/rs/computebench/ComputeBench.java
new file mode 100644
index 0000000..2d3e843
--- /dev/null
+++ b/tests/RenderScriptTests/ComputeBenchmark/src/com/example/android/rs/computebench/ComputeBench.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.rs.computebench;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.renderscript.RenderScript;
+
+public class ComputeBench extends Activity {
+ private RenderScript mRS;
+ private Benchmark mBenchmark;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ mRS = RenderScript.create(this);
+
+ mBenchmark = new Benchmark(mRS, getResources());
+ mBenchmark.run();
+ }
+}
diff --git a/tests/RenderScriptTests/ComputeBenchmark/src/com/example/android/rs/computebench/compute_benchmark.rs b/tests/RenderScriptTests/ComputeBenchmark/src/com/example/android/rs/computebench/compute_benchmark.rs
new file mode 100644
index 0000000..7b8ec04
--- /dev/null
+++ b/tests/RenderScriptTests/ComputeBenchmark/src/com/example/android/rs/computebench/compute_benchmark.rs
@@ -0,0 +1,408 @@
+// 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.
+
+#pragma version(1)
+#pragma rs java_package_name(com.example.android.rs.computebench)
+
+// Test configuration (accessible from Java)
+uint priming_runs = 1000000;
+uint timing_runs = 5000000;
+
+// Reused variables
+
+static volatile int64_t bench_time;
+static float inv_timing_runs;
+
+#define DECL_VAR_SET(prefix) \
+static volatile float prefix##_f_1 = 1; \
+static volatile float2 prefix##_f_2 = 1; \
+static volatile float3 prefix##_f_3 = 1; \
+static volatile float4 prefix##_f_4 = 1; \
+static volatile char prefix##_c_1 = 1; \
+static volatile char2 prefix##_c_2 = 1; \
+static volatile char3 prefix##_c_3 = 1; \
+static volatile char4 prefix##_c_4 = 1; \
+static volatile uchar prefix##_uc_1 = 1; \
+static volatile uchar2 prefix##_uc_2 = 1; \
+static volatile uchar3 prefix##_uc_3 = 1; \
+static volatile uchar4 prefix##_uc_4 = 1; \
+static volatile short prefix##_s_1 = 1; \
+static volatile short2 prefix##_s_2 = 1; \
+static volatile short3 prefix##_s_3 = 1; \
+static volatile short4 prefix##_s_4 = 1; \
+static volatile ushort prefix##_us_1 = 1; \
+static volatile ushort2 prefix##_us_2 = 1; \
+static volatile ushort3 prefix##_us_3 = 1; \
+static volatile ushort4 prefix##_us_4 = 1; \
+static volatile int prefix##_i_1 = 1; \
+static volatile int2 prefix##_i_2 = 1; \
+static volatile int3 prefix##_i_3 = 1; \
+static volatile int4 prefix##_i_4 = 1; \
+static volatile uint prefix##_ui_1 = 1; \
+static volatile uint2 prefix##_ui_2 = 1; \
+static volatile uint3 prefix##_ui_3 = 1; \
+static volatile uint4 prefix##_ui_4 = 1; \
+static volatile long prefix##_l_1 = 1; \
+static volatile long2 prefix##_l_2 = 1; \
+static volatile long3 prefix##_l_3 = 1; \
+static volatile long4 prefix##_l_4 = 1; \
+static volatile ulong prefix##_ul_1 = 1; \
+static volatile ulong2 prefix##_ul_2 = 1; \
+static volatile ulong3 prefix##_ul_3 = 1; \
+static volatile ulong4 prefix##_ul_4 = 1; \
+
+DECL_VAR_SET(res)
+DECL_VAR_SET(src1)
+DECL_VAR_SET(src2)
+DECL_VAR_SET(src3)
+
+
+// Testing macros
+
+#define RUN_BENCH(line, op) \
+ for (int i = priming_runs - 1; i >= 0; --i) { \
+ line; \
+ } \
+ bench_time = rsUptimeMillis(); \
+ for (int i = timing_runs - 1; i >= 0; --i) { \
+ line; \
+ } \
+ bench_time = rsUptimeMillis() - bench_time; \
+ rsDebug(" " op " took ns", (float)bench_time * inv_timing_runs);
+
+#define BENCH_BASIC_OP_TYPE(op, type) \
+ RUN_BENCH(res_##type##_1 = src1_##type##_1 op src2_##type##_1, #type "1 " #op " " #type "1") \
+ RUN_BENCH(res_##type##_2 = src1_##type##_2 op src2_##type##_2, #type "2 " #op " " #type "2") \
+ RUN_BENCH(res_##type##_3 = src1_##type##_3 op src2_##type##_3, #type "3 " #op " " #type "3") \
+ RUN_BENCH(res_##type##_4 = src1_##type##_4 op src2_##type##_4, #type "4 " #op " " #type "4") \
+
+#define BENCH_BASIC_INT_OP(op) \
+ rsDebug("Testing basic operation " #op, 0); \
+ BENCH_BASIC_OP_TYPE(op, c) \
+ BENCH_BASIC_OP_TYPE(op, uc) \
+ BENCH_BASIC_OP_TYPE(op, s) \
+ BENCH_BASIC_OP_TYPE(op, us) \
+ BENCH_BASIC_OP_TYPE(op, i) \
+ BENCH_BASIC_OP_TYPE(op, ui) \
+ RUN_BENCH(res_l_1 = src1_l_1 op src2_l_1, "l1 " #op " l1") \
+ RUN_BENCH(res_ul_1 = src1_ul_1 op src2_ul_1, "ul1 " #op " ul1")
+
+#define BENCH_BASIC_OP(op) \
+ BENCH_BASIC_INT_OP(op) \
+ BENCH_BASIC_OP_TYPE(op, f)
+
+#define BENCH_CVT(to, from, type) \
+ rsDebug("Testing convert from " #from " to " #to, 0); \
+ RUN_BENCH(res_##to##_1 = (type)src1_##from##_1, "(" #to ")" #from) \
+ RUN_BENCH(res_##to##_2 = convert_##type##2(src1_##from##_2), #to "2 convert_" #type "2(" #from "2)") \
+ RUN_BENCH(res_##to##_3 = convert_##type##3(src1_##from##_3), #to "3 convert_" #type "3(" #from "3)") \
+ RUN_BENCH(res_##to##_4 = convert_##type##4(src1_##from##_4), #to "4 convert_" #type "4(" #from "4)")
+
+#define BENCH_CVT_MATRIX(to, type) \
+ BENCH_CVT(to, c, type); \
+ BENCH_CVT(to, uc, type); \
+ BENCH_CVT(to, s, type); \
+ BENCH_CVT(to, us, type); \
+ BENCH_CVT(to, i, type); \
+ BENCH_CVT(to, ui, type); \
+ BENCH_CVT(to, f, type); \
+
+#define BENCH_XN_FUNC_YN(typeout, fnc, typein) \
+ RUN_BENCH(res_##typeout##_1 = fnc(src1_##typein##_1);, #typeout "1 " #fnc "(" #typein "1)") \
+ RUN_BENCH(res_##typeout##_2 = fnc(src1_##typein##_2);, #typeout "2 " #fnc "(" #typein "2)") \
+ RUN_BENCH(res_##typeout##_3 = fnc(src1_##typein##_3);, #typeout "3 " #fnc "(" #typein "3)") \
+ RUN_BENCH(res_##typeout##_4 = fnc(src1_##typein##_4);, #typeout "4 " #fnc "(" #typein "4)")
+
+#define BENCH_XN_FUNC_XN_XN(type, fnc) \
+ RUN_BENCH(res_##type##_1 = fnc(src1_##type##_1, src2_##type##_1), #type "1 " #fnc "(" #type "1, " #type "1)") \
+ RUN_BENCH(res_##type##_2 = fnc(src1_##type##_2, src2_##type##_2), #type "2 " #fnc "(" #type "2, " #type "2)") \
+ RUN_BENCH(res_##type##_3 = fnc(src1_##type##_3, src2_##type##_3), #type "3 " #fnc "(" #type "3, " #type "3)") \
+ RUN_BENCH(res_##type##_4 = fnc(src1_##type##_4, src2_##type##_4), #type "4 " #fnc "(" #type "4, " #type "4)") \
+
+#define BENCH_X_FUNC_X_X_X(type, fnc) \
+ RUN_BENCH(res_##type##_1 = fnc(src1_##type##_1, src2_##type##_1, src3_##type##_1), #type "1 " #fnc "(" #type "1, " #type "1, " #type "1)")
+
+#define BENCH_IN_FUNC_IN(fnc) \
+ rsDebug("Testing " #fnc, 0); \
+ BENCH_XN_FUNC_YN(uc, fnc, uc) \
+ BENCH_XN_FUNC_YN(c, fnc, c) \
+ BENCH_XN_FUNC_YN(us, fnc, us) \
+ BENCH_XN_FUNC_YN(s, fnc, s) \
+ BENCH_XN_FUNC_YN(ui, fnc, ui) \
+ BENCH_XN_FUNC_YN(i, fnc, i)
+
+#define BENCH_UIN_FUNC_IN(fnc) \
+ rsDebug("Testing " #fnc, 0); \
+ BENCH_XN_FUNC_YN(uc, fnc, c) \
+ BENCH_XN_FUNC_YN(us, fnc, s) \
+ BENCH_XN_FUNC_YN(ui, fnc, i) \
+
+#define BENCH_IN_FUNC_IN_IN(fnc) \
+ rsDebug("Testing " #fnc, 0); \
+ BENCH_XN_FUNC_XN_XN(uc, fnc) \
+ BENCH_XN_FUNC_XN_XN(c, fnc) \
+ BENCH_XN_FUNC_XN_XN(us, fnc) \
+ BENCH_XN_FUNC_XN_XN(s, fnc) \
+ BENCH_XN_FUNC_XN_XN(ui, fnc) \
+ BENCH_XN_FUNC_XN_XN(i, fnc)
+
+#define BENCH_I_FUNC_I_I_I(fnc) \
+ rsDebug("Testing " #fnc, 0); \
+ BENCH_X_FUNC_X_X_X(uc, fnc) \
+ BENCH_X_FUNC_X_X_X(c, fnc) \
+ BENCH_X_FUNC_X_X_X(us, fnc) \
+ BENCH_X_FUNC_X_X_X(s, fnc) \
+ BENCH_X_FUNC_X_X_X(ui, fnc) \
+ BENCH_X_FUNC_X_X_X(i, fnc)
+
+#define BENCH_FN_FUNC_FN(fnc) \
+ rsDebug("Testing " #fnc, 0); \
+ RUN_BENCH(res_f_1 = fnc(src1_f_1), "f1 " #fnc "(f1)") \
+ RUN_BENCH(res_f_2 = fnc(src1_f_2), "f2 " #fnc "(f2)") \
+ RUN_BENCH(res_f_3 = fnc(src1_f_3), "f3 " #fnc "(f3)") \
+ RUN_BENCH(res_f_4 = fnc(src1_f_4), "f4 " #fnc "(f4)")
+
+#define BENCH_FN_FUNC_FN_PFN(fnc) \
+ rsDebug("Testing " #fnc, 0); \
+ RUN_BENCH(res_f_1 = fnc(src1_f_1, (float*) &src2_f_1), "f1 " #fnc "(f1, f1*)") \
+ RUN_BENCH(res_f_2 = fnc(src1_f_2, (float2*) &src2_f_2), "f2 " #fnc "(f2, f2*)") \
+ RUN_BENCH(res_f_3 = fnc(src1_f_3, (float3*) &src2_f_3), "f3 " #fnc "(f3, f3*)") \
+ RUN_BENCH(res_f_4 = fnc(src1_f_4, (float4*) &src2_f_4), "f4 " #fnc "(f4, f4*)")
+
+#define BENCH_FN_FUNC_FN_FN(fnc) \
+ rsDebug("Testing " #fnc, 0); \
+ RUN_BENCH(res_f_1 = fnc(src1_f_1, src2_f_1), "f1 " #fnc "(f1, f1)") \
+ RUN_BENCH(res_f_2 = fnc(src1_f_2, src2_f_2), "f2 " #fnc "(f2, f2)") \
+ RUN_BENCH(res_f_3 = fnc(src1_f_3, src2_f_3), "f3 " #fnc "(f3, f3)") \
+ RUN_BENCH(res_f_4 = fnc(src1_f_4, src2_f_4), "f4 " #fnc "(f4, f4)")
+
+#define BENCH_F34_FUNC_F34_F34(fnc) \
+ rsDebug("Testing " #fnc, 0); \
+ RUN_BENCH(res_f_3 = fnc(src1_f_3, src2_f_3), "f3 " #fnc "(f3, f3)") \
+ RUN_BENCH(res_f_4 = fnc(src1_f_4, src2_f_4), "f4 " #fnc "(f4, f4)")
+
+#define BENCH_FN_FUNC_FN_F(fnc) \
+ rsDebug("Testing " #fnc, 0); \
+ RUN_BENCH(res_f_1 = fnc(src1_f_1, src2_f_1), "f1 " #fnc "(f1, f1)") \
+ RUN_BENCH(res_f_2 = fnc(src1_f_2, src2_f_1), "f2 " #fnc "(f2, f1)") \
+ RUN_BENCH(res_f_3 = fnc(src1_f_3, src2_f_1), "f3 " #fnc "(f3, f1)") \
+ RUN_BENCH(res_f_4 = fnc(src1_f_4, src2_f_1), "f4 " #fnc "(f4, f1)")
+
+#define BENCH_F_FUNC_FN(fnc) \
+ rsDebug("Testing " #fnc, 0); \
+ RUN_BENCH(res_f_1 = fnc(src1_f_1), "f1 " #fnc "(f1)") \
+ RUN_BENCH(res_f_1 = fnc(src1_f_2), "f1 " #fnc "(f2)") \
+ RUN_BENCH(res_f_1 = fnc(src1_f_3), "f1 " #fnc "(f3)") \
+ RUN_BENCH(res_f_1 = fnc(src1_f_4), "f1 " #fnc "(f4)")
+
+#define BENCH_F_FUNC_FN_FN(fnc) \
+ rsDebug("Testing " #fnc, 0); \
+ RUN_BENCH(res_f_1 = fnc(src1_f_1, src2_f_1), "f1 " #fnc "(f1, f1)") \
+ RUN_BENCH(res_f_1 = fnc(src1_f_2, src2_f_2), "f1 " #fnc "(f2, f2)") \
+ RUN_BENCH(res_f_1 = fnc(src1_f_3, src2_f_3), "f1 " #fnc "(f3, f3)") \
+ RUN_BENCH(res_f_1 = fnc(src1_f_4, src2_f_4), "f1 " #fnc "(f4, f4)")
+
+#define BENCH_FN_FUNC_FN_IN(fnc) \
+ rsDebug("Testing " #fnc, 0); \
+ RUN_BENCH(res_f_1 = fnc(src1_f_1, src1_i_1), "f1 " #fnc "(f1, i1)") \
+ RUN_BENCH(res_f_2 = fnc(src1_f_2, src1_i_2), "f2 " #fnc "(f2, i2)") \
+ RUN_BENCH(res_f_3 = fnc(src1_f_3, src1_i_3), "f3 " #fnc "(f3, i3)") \
+ RUN_BENCH(res_f_4 = fnc(src1_f_4, src1_i_4), "f4 " #fnc "(f4, i4)")
+
+#define BENCH_FN_FUNC_FN_I(fnc) \
+ rsDebug("Testing " #fnc, 0); \
+ RUN_BENCH(res_f_1 = fnc(src1_f_1, src1_i_1), "f1 " #fnc "(f1, i1)") \
+ RUN_BENCH(res_f_2 = fnc(src1_f_2, src1_i_1), "f2 " #fnc "(f2, i1)") \
+ RUN_BENCH(res_f_3 = fnc(src1_f_3, src1_i_1), "f3 " #fnc "(f3, i1)") \
+ RUN_BENCH(res_f_4 = fnc(src1_f_4, src1_i_1), "f4 " #fnc "(f4, i1)")
+
+#define BENCH_FN_FUNC_FN_FN_FN(fnc) \
+ rsDebug("Testing " #fnc, 0); \
+ RUN_BENCH(res_f_1 = fnc(src1_f_1, src2_f_1, src3_f_1), "f1 " #fnc "(f1, f1, f1)") \
+ RUN_BENCH(res_f_2 = fnc(src1_f_2, src2_f_2, src3_f_2), "f2 " #fnc "(f2, f2, f2)") \
+ RUN_BENCH(res_f_3 = fnc(src1_f_3, src2_f_3, src3_f_3), "f3 " #fnc "(f3, f3, f3)") \
+ RUN_BENCH(res_f_4 = fnc(src1_f_4, src2_f_4, src3_f_4), "f4 " #fnc "(f4, f4, f4)")
+
+#define BENCH_FN_FUNC_FN_FN_F(fnc) \
+ rsDebug("Testing " #fnc, 0); \
+ RUN_BENCH(res_f_1 = fnc(src1_f_1, src2_f_1, src3_f_1), "f1 " #fnc "(f1, f1, f1)") \
+ RUN_BENCH(res_f_2 = fnc(src1_f_2, src2_f_2, src3_f_1), "f2 " #fnc "(f2, f2, f1)") \
+ RUN_BENCH(res_f_3 = fnc(src1_f_3, src2_f_3, src3_f_1), "f3 " #fnc "(f3, f3, f1)") \
+ RUN_BENCH(res_f_4 = fnc(src1_f_4, src2_f_4, src3_f_1), "f4 " #fnc "(f4, f4, f1)")
+
+#define BENCH_FN_FUNC_FN_PIN(fnc) \
+ rsDebug("Testing " #fnc, 0); \
+ RUN_BENCH(res_f_1 = fnc(src1_f_1, (int*) &src1_i_1), "f1 " #fnc "(f1, i1*)") \
+ RUN_BENCH(res_f_2 = fnc(src1_f_2, (int2*) &src1_i_2), "f2 " #fnc "(f2, i2*)") \
+ RUN_BENCH(res_f_3 = fnc(src1_f_3, (int3*) &src1_i_3), "f3 " #fnc "(f3, i3*)") \
+ RUN_BENCH(res_f_4 = fnc(src1_f_4, (int4*) &src1_i_4), "f4 " #fnc "(f4, i4*)")
+
+#define BENCH_FN_FUNC_FN_FN_PIN(fnc) \
+ rsDebug("Testing " #fnc, 0); \
+ RUN_BENCH(res_f_1 = fnc(src1_f_1, src2_f_1, (int*) &src1_i_1), "f1 " #fnc "(f1, f1, i1*)") \
+ RUN_BENCH(res_f_2 = fnc(src1_f_2, src2_f_2, (int2*) &src1_i_2), "f2 " #fnc "(f2, f2, i2*)") \
+ RUN_BENCH(res_f_3 = fnc(src1_f_3, src2_f_3, (int3*) &src1_i_3), "f3 " #fnc "(f3, f3, i3*)") \
+ RUN_BENCH(res_f_4 = fnc(src1_f_4, src2_f_4, (int4*) &src1_i_4), "f4 " #fnc "(f4, f4, i4*)")
+
+#define BENCH_IN_FUNC_FN(fnc) \
+ rsDebug("Testing " #fnc, 0); \
+ RUN_BENCH(res_i_1 = fnc(src1_f_1), "i1 " #fnc "(f1)") \
+ RUN_BENCH(res_i_2 = fnc(src1_f_2), "i2 " #fnc "(f2)") \
+ RUN_BENCH(res_i_3 = fnc(src1_f_3), "i3 " #fnc "(f3)") \
+ RUN_BENCH(res_i_4 = fnc(src1_f_4), "i4 " #fnc "(f4)")
+
+
+// Testing functions
+
+static void bench_basic_operators() {
+ int i = 0;
+ BENCH_BASIC_OP(+);
+ BENCH_BASIC_OP(-);
+ BENCH_BASIC_OP(*);
+ BENCH_BASIC_OP(/);
+ BENCH_BASIC_INT_OP(%);
+ BENCH_BASIC_INT_OP(<<);
+ BENCH_BASIC_INT_OP(>>);
+}
+
+static void bench_convert() {
+ BENCH_CVT_MATRIX(c, char);
+ BENCH_CVT_MATRIX(uc, uchar);
+ BENCH_CVT_MATRIX(s, short);
+ BENCH_CVT_MATRIX(us, ushort);
+ BENCH_CVT_MATRIX(i, int);
+ BENCH_CVT_MATRIX(ui, uint);
+ BENCH_CVT_MATRIX(f, float);
+}
+
+static void bench_int_math() {
+ BENCH_UIN_FUNC_IN(abs);
+ BENCH_IN_FUNC_IN(clz);
+ BENCH_IN_FUNC_IN_IN(min);
+ BENCH_IN_FUNC_IN_IN(max);
+ BENCH_I_FUNC_I_I_I(rsClamp);
+}
+
+static void bench_fp_math() {
+ BENCH_FN_FUNC_FN(acos);
+ BENCH_FN_FUNC_FN(acosh);
+ BENCH_FN_FUNC_FN(acospi);
+ BENCH_FN_FUNC_FN(asin);
+ BENCH_FN_FUNC_FN(asinh);
+ BENCH_FN_FUNC_FN(asinpi);
+ BENCH_FN_FUNC_FN(atan);
+ BENCH_FN_FUNC_FN_FN(atan2);
+ BENCH_FN_FUNC_FN(atanh);
+ BENCH_FN_FUNC_FN(atanpi);
+ BENCH_FN_FUNC_FN_FN(atan2pi);
+ BENCH_FN_FUNC_FN(cbrt);
+ BENCH_FN_FUNC_FN(ceil);
+ BENCH_FN_FUNC_FN_FN_FN(clamp);
+ BENCH_FN_FUNC_FN_FN_F(clamp);
+ BENCH_FN_FUNC_FN_FN(copysign);
+ BENCH_FN_FUNC_FN(cos);
+ BENCH_FN_FUNC_FN(cosh);
+ BENCH_FN_FUNC_FN(cospi);
+ BENCH_F34_FUNC_F34_F34(cross);
+ BENCH_FN_FUNC_FN(degrees);
+ BENCH_F_FUNC_FN_FN(distance);
+ BENCH_F_FUNC_FN_FN(dot);
+ BENCH_FN_FUNC_FN(erfc);
+ BENCH_FN_FUNC_FN(erf);
+ BENCH_FN_FUNC_FN(exp);
+ BENCH_FN_FUNC_FN(exp2);
+ BENCH_FN_FUNC_FN(exp10);
+ BENCH_FN_FUNC_FN(expm1);
+ BENCH_FN_FUNC_FN(fabs);
+ BENCH_FN_FUNC_FN_FN(fdim);
+ BENCH_FN_FUNC_FN(floor);
+ BENCH_FN_FUNC_FN_FN_FN(fma);
+ BENCH_FN_FUNC_FN_FN(fmax);
+ BENCH_FN_FUNC_FN_F(fmax);
+ BENCH_FN_FUNC_FN_FN(fmin);
+ BENCH_FN_FUNC_FN_F(fmin);
+ BENCH_FN_FUNC_FN_FN(fmod);
+ BENCH_FN_FUNC_FN_PFN(fract);
+ BENCH_FN_FUNC_FN_PIN(frexp);
+ BENCH_FN_FUNC_FN_FN(hypot);
+ BENCH_IN_FUNC_FN(ilogb);
+ BENCH_FN_FUNC_FN_IN(ldexp);
+ BENCH_FN_FUNC_FN_I(ldexp);
+ BENCH_F_FUNC_FN(length);
+ BENCH_FN_FUNC_FN(lgamma);
+ BENCH_FN_FUNC_FN_PIN(lgamma);
+ BENCH_FN_FUNC_FN(log);
+ BENCH_FN_FUNC_FN(log2);
+ BENCH_FN_FUNC_FN(log10);
+ BENCH_FN_FUNC_FN(log1p);
+ BENCH_FN_FUNC_FN(logb);
+ BENCH_FN_FUNC_FN_FN_FN(mad);
+ BENCH_FN_FUNC_FN_FN(max);
+ BENCH_FN_FUNC_FN_F(max);
+ BENCH_FN_FUNC_FN_FN(min);
+ BENCH_FN_FUNC_FN_F(min);
+ BENCH_FN_FUNC_FN_FN_FN(mix);
+ BENCH_FN_FUNC_FN_FN_F(mix);
+ BENCH_FN_FUNC_FN_PFN(modf);
+ BENCH_FN_FUNC_FN_FN(nextafter);
+ BENCH_FN_FUNC_FN(normalize);
+ BENCH_FN_FUNC_FN_FN(pow);
+ BENCH_FN_FUNC_FN_IN(pown);
+ BENCH_FN_FUNC_FN_FN(powr);
+ BENCH_FN_FUNC_FN(radians);
+ BENCH_FN_FUNC_FN_FN(remainder);
+ BENCH_FN_FUNC_FN_FN_PIN(remquo);
+ BENCH_FN_FUNC_FN(rint);
+ BENCH_FN_FUNC_FN_IN(rootn);
+ BENCH_FN_FUNC_FN(round);
+ BENCH_FN_FUNC_FN(rsqrt);
+ BENCH_FN_FUNC_FN(sign);
+ BENCH_FN_FUNC_FN(sin);
+ BENCH_FN_FUNC_FN_PFN(sincos);
+ BENCH_FN_FUNC_FN(sinh);
+ BENCH_FN_FUNC_FN(sinpi);
+ BENCH_FN_FUNC_FN(sqrt);
+ BENCH_FN_FUNC_FN_FN(step);
+ BENCH_FN_FUNC_FN_F(step);
+ BENCH_FN_FUNC_FN(tan);
+ BENCH_FN_FUNC_FN(tanh);
+ BENCH_FN_FUNC_FN(tanpi);
+ BENCH_FN_FUNC_FN(tgamma);
+ BENCH_FN_FUNC_FN(trunc);
+}
+
+static void bench_approx_math() {
+ BENCH_FN_FUNC_FN(approx_recip);
+ BENCH_FN_FUNC_FN(approx_sqrt);
+ BENCH_FN_FUNC_FN(approx_rsqrt);
+ BENCH_FN_FUNC_FN(approx_length);
+ BENCH_FN_FUNC_FN_FN(approx_distance);
+ BENCH_FN_FUNC_FN(approx_normalize);
+ BENCH_FN_FUNC_FN(approx_atan);
+}
+
+void bench() {
+ rsDebug("RS Compute Benchmark", 0);
+ rsDebug("Current configuration:", 0);
+ rsDebug("Priming runs", priming_runs);
+ rsDebug("Timing runs", timing_runs);
+ rsDebug("Beginning test", 0);
+ inv_timing_runs = 1000000.f / (float)timing_runs;
+ bench_basic_operators();
+ bench_convert();
+ bench_int_math();
+ bench_fp_math();
+ bench_approx_math();
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Fisheye.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Fisheye.java
index bf68f91..81868b10 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Fisheye.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Fisheye.java
@@ -26,12 +26,16 @@
public class Fisheye extends TestBase {
private ScriptC_fisheye_full mScript_full = null;
private ScriptC_fisheye_relaxed mScript_relaxed = null;
+ private ScriptC_fisheye_approx_full mScript_approx_full = null;
+ private ScriptC_fisheye_approx_relaxed mScript_approx_relaxed = null;
+ private final boolean approx;
private final boolean relaxed;
private float center_x = 0.5f;
private float center_y = 0.5f;
private float scale = 0.5f;
- public Fisheye(boolean relaxed) {
+ public Fisheye(boolean approx, boolean relaxed) {
+ this.approx = approx;
this.relaxed = relaxed;
}
@@ -68,7 +72,18 @@
}
private void do_init() {
- if (relaxed)
+ if (approx) {
+ if (relaxed)
+ mScript_approx_relaxed.invoke_init_filter(
+ mInPixelsAllocation.getType().getX(),
+ mInPixelsAllocation.getType().getY(), center_x,
+ center_y, scale);
+ else
+ mScript_approx_full.invoke_init_filter(
+ mInPixelsAllocation.getType().getX(),
+ mInPixelsAllocation.getType().getY(), center_x,
+ center_y, scale);
+ } else if (relaxed)
mScript_relaxed.invoke_init_filter(
mInPixelsAllocation.getType().getX(),
mInPixelsAllocation.getType().getY(), center_x, center_y,
@@ -81,7 +96,19 @@
}
public void createTest(android.content.res.Resources res) {
- if (relaxed) {
+ if (approx) {
+ if (relaxed) {
+ mScript_approx_relaxed = new ScriptC_fisheye_approx_relaxed(mRS,
+ res, R.raw.fisheye_approx_relaxed);
+ mScript_approx_relaxed.set_in_alloc(mInPixelsAllocation);
+ mScript_approx_relaxed.set_sampler(Sampler.CLAMP_LINEAR(mRS));
+ } else {
+ mScript_approx_full = new ScriptC_fisheye_approx_full(mRS, res,
+ R.raw.fisheye_approx_full);
+ mScript_approx_full.set_in_alloc(mInPixelsAllocation);
+ mScript_approx_full.set_sampler(Sampler.CLAMP_LINEAR(mRS));
+ }
+ } else if (relaxed) {
mScript_relaxed = new ScriptC_fisheye_relaxed(mRS, res,
R.raw.fisheye_relaxed);
mScript_relaxed.set_in_alloc(mInPixelsAllocation);
@@ -96,7 +123,12 @@
}
public void runTest() {
- if (relaxed)
+ if (approx) {
+ if (relaxed)
+ mScript_approx_relaxed.forEach_root(mOutPixelsAllocation);
+ else
+ mScript_approx_full.forEach_root(mOutPixelsAllocation);
+ } else if (relaxed)
mScript_relaxed.forEach_root(mOutPixelsAllocation);
else
mScript_full.forEach_root(mOutPixelsAllocation);
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
index 3eec7f5..07626a3 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
@@ -144,30 +144,36 @@
mTest = new Grain();
break;
case 7:
- mTest = new Fisheye(false);
+ mTest = new Fisheye(false, false);
break;
case 8:
- mTest = new Fisheye(true);
+ mTest = new Fisheye(false, true);
break;
case 9:
- mTest = new Vignette(false, false);
+ mTest = new Fisheye(true, false);
break;
case 10:
- mTest = new Vignette(false, true);
+ mTest = new Fisheye(true, true);
break;
case 11:
- mTest = new Vignette(true, false);
+ mTest = new Vignette(false, false);
break;
case 12:
- mTest = new Vignette(true, true);
+ mTest = new Vignette(false, true);
break;
case 13:
- mTest = new GroupTest(true);
+ mTest = new Vignette(true, false);
break;
case 14:
- mTest = new GroupTest(false);
+ mTest = new Vignette(true, true);
break;
case 15:
+ mTest = new GroupTest(true);
+ break;
+ case 16:
+ mTest = new GroupTest(false);
+ break;
+ case 17:
mTest = new Intrinsics(0);
break;
}
@@ -182,7 +188,7 @@
}
void setupTests() {
- mTestNames = new String[16];
+ mTestNames = new String[18];
mTestNames[0] = "Levels Vec3 Relaxed";
mTestNames[1] = "Levels Vec4 Relaxed";
mTestNames[2] = "Levels Vec3 Full";
@@ -192,13 +198,15 @@
mTestNames[6] = "Grain";
mTestNames[7] = "Fisheye Full";
mTestNames[8] = "Fisheye Relaxed";
- mTestNames[9] = "Vignette Full";
- mTestNames[10] = "Vignette Relaxed";
- mTestNames[11] = "Vignette Approximate Full";
- mTestNames[12] = "Vignette Approximate Relaxed";
- mTestNames[13] = "Group Test (emulated)";
- mTestNames[14] = "Group Test (native)";
- mTestNames[15] = "Intrinsics Convolve 3x3";
+ mTestNames[9] = "Fisheye Approximate Full";
+ mTestNames[10] = "Fisheye Approximate Relaxed";
+ mTestNames[11] = "Vignette Full";
+ mTestNames[12] = "Vignette Relaxed";
+ mTestNames[13] = "Vignette Approximate Full";
+ mTestNames[14] = "Vignette Approximate Relaxed";
+ mTestNames[15] = "Group Test (emulated)";
+ mTestNames[16] = "Group Test (native)";
+ mTestNames[17] = "Intrinsics Convolve 3x3";
mTestSpinner.setAdapter(new ArrayAdapter<String>(
this, R.layout.spinner_layout, mTestNames));
}
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye.rsh b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye.rsh
index 4dcfc1d..3809912 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye.rsh
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye.rsh
@@ -17,46 +17,41 @@
rs_allocation in_alloc;
rs_sampler sampler;
-static float2 center, dimensions;
-static float2 scale;
-static float alpha;
-static float radius2;
-static float factor;
+static float2 center, neg_center, inv_dimensions, axis_scale;
+static float alpha, radius2, factor;
-void init_filter(uint32_t dim_x, uint32_t dim_y, float focus_x, float focus_y, float k) {
- center.x = focus_x;
- center.y = focus_y;
- dimensions.x = (float)dim_x;
- dimensions.y = (float)dim_y;
-
+void init_filter(uint32_t dim_x, uint32_t dim_y, float center_x, float center_y, float k) {
+ center.x = center_x;
+ center.y = center_y;
+ neg_center = -center;
+ inv_dimensions.x = 1.f / (float)dim_x;
+ inv_dimensions.y = 1.f / (float)dim_y;
alpha = k * 2.0 + 0.75;
- float bound2 = 0.25;
- if (dim_x > dim_y) {
- scale.x = 1.0;
- scale.y = dimensions.y / dimensions.x;
- bound2 *= (scale.y*scale.y + 1);
- } else {
- scale.x = dimensions.x / dimensions.y;
- scale.y = 1.0;
- bound2 *= (scale.x*scale.x + 1);
- }
+
+ axis_scale = (float2)1.f;
+ if (dim_x > dim_y)
+ axis_scale.y = (float)dim_y / (float)dim_x;
+ else
+ axis_scale.x = (float)dim_x / (float)dim_y;
+
+ const float bound2 = 0.25 * (axis_scale.x*axis_scale.x + axis_scale.y*axis_scale.y);
const float bound = sqrt(bound2);
const float radius = 1.15 * bound;
radius2 = radius*radius;
- const float max_radian = 0.5f * M_PI - atan(alpha / bound * sqrt(radius2 - bound2));
+ const float max_radian = M_PI_2 - atan(alpha / bound * sqrt(radius2 - bound2));
factor = bound / max_radian;
}
void root(uchar4 *out, uint32_t x, uint32_t y) {
// Convert x and y to floating point coordinates with center as origin
- float2 coord;
- coord.x = (float)x / dimensions.x;
- coord.y = (float)y / dimensions.y;
- coord -= center;
- const float dist = length(scale * coord);
- const float radian = M_PI_2 - atan((alpha * sqrt(radius2 - dist * dist)) / dist);
- const float scalar = radian * factor / dist;
- const float2 new_coord = coord * scalar + center;
+ const float2 inCoord = {(float)x, (float)y};
+ const float2 coord = mad(inCoord, inv_dimensions, neg_center);
+ const float2 scaledCoord = axis_scale * coord;
+ const float dist2 = scaledCoord.x*scaledCoord.x + scaledCoord.y*scaledCoord.y;
+ const float inv_dist = rsqrt(dist2);
+ const float radian = M_PI_2 - atan((alpha * sqrt(radius2 - dist2)) * inv_dist);
+ const float scalar = radian * factor * inv_dist;
+ const float2 new_coord = mad(coord, scalar, center);
const float4 fout = rsSample(in_alloc, sampler, new_coord);
*out = rsPackColorTo8888(fout);
}
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_approx.rsh b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_approx.rsh
new file mode 100644
index 0000000..008acbe
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_approx.rsh
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+rs_allocation in_alloc;
+rs_sampler sampler;
+
+static float2 center, neg_center, inv_dimensions, axis_scale;
+static float alpha, radius2, factor;
+
+void init_filter(uint32_t dim_x, uint32_t dim_y, float center_x, float center_y, float k) {
+ center.x = center_x;
+ center.y = center_y;
+ neg_center = -center;
+ inv_dimensions.x = 1.f / (float)dim_x;
+ inv_dimensions.y = 1.f / (float)dim_y;
+ alpha = k * 2.0 + 0.75;
+
+ axis_scale = (float2)1.f;
+ if (dim_x > dim_y)
+ axis_scale.y = (float)dim_y / (float)dim_x;
+ else
+ axis_scale.x = (float)dim_x / (float)dim_y;
+
+ const float bound2 = 0.25 * (axis_scale.x*axis_scale.x + axis_scale.y*axis_scale.y);
+ const float bound = sqrt(bound2);
+ const float radius = 1.15 * bound;
+ radius2 = radius*radius;
+ const float max_radian = M_PI_2 - atan(alpha / bound * sqrt(radius2 - bound2));
+ factor = bound / max_radian;
+}
+
+void root(uchar4 *out, uint32_t x, uint32_t y) {
+ // Convert x and y to floating point coordinates with center as origin
+ const float2 inCoord = {(float)x, (float)y};
+ const float2 coord = mad(inCoord, inv_dimensions, neg_center);
+ const float2 scaledCoord = axis_scale * coord;
+ const float dist2 = scaledCoord.x*scaledCoord.x + scaledCoord.y*scaledCoord.y;
+ const float inv_dist = approx_rsqrt(dist2);
+ const float radian = M_PI_2 - approx_atan((alpha * approx_sqrt(radius2 - dist2)) * inv_dist);
+ const float scalar = radian * factor * inv_dist;
+ const float2 new_coord = mad(coord, scalar, center);
+ const float4 fout = rsSample(in_alloc, sampler, new_coord);
+ *out = rsPackColorTo8888(fout);
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_approx_full.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_approx_full.rs
new file mode 100644
index 0000000..1ea37db
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_approx_full.rs
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.image)
+
+#include "fisheye_approx.rsh"
+
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_approx_relaxed.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_approx_relaxed.rs
new file mode 100644
index 0000000..3e76368
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_approx_relaxed.rs
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.image)
+#pragma rs_fp_relaxed
+
+#include "fisheye_approx.rsh"
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/Android.mk b/tests/RenderScriptTests/ImageProcessing2/Android.mk
new file mode 100644
index 0000000..c81fd93
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/Android.mk
@@ -0,0 +1,35 @@
+#
+# Copyright (C) 2009 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+ $(call all-renderscript-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := android.support.v8.renderscript
+
+LOCAL_PACKAGE_NAME := ImageProcessing2
+
+LOCAL_RENDERSCRIPT_FLAGS := -rs-package-name=android.support.v8.renderscript
+LOCAL_REQUIRED_MODULES := librsjni
+
+include $(BUILD_PACKAGE)
+
+#include $(call all-makefiles-under, $(LOCAL_PATH))
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/AndroidManifest.xml b/tests/RenderScriptTests/ImageProcessing2/AndroidManifest.xml
new file mode 100644
index 0000000..1ef04c2
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/AndroidManifest.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.rs.image2">
+ <uses-sdk android:minSdkVersion="11" />
+ <application android:label="IP GB"
+ android:hardwareAccelerated="true">
+ <activity android:name="ImageProcessingActivity2">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/RenderScriptTests/ImageProcessing2/res/drawable-nodpi/city.png b/tests/RenderScriptTests/ImageProcessing2/res/drawable-nodpi/city.png
new file mode 100644
index 0000000..856eeff
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/res/drawable-nodpi/city.png
Binary files differ
diff --git a/tests/RenderScriptTests/ImageProcessing2/res/layout/main.xml b/tests/RenderScriptTests/ImageProcessing2/res/layout/main.xml
new file mode 100644
index 0000000..bd56d62
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/res/layout/main.xml
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:id="@+id/toplevel">
+ <SurfaceView
+ android:id="@+id/surface"
+ android:layout_width="1dip"
+ android:layout_height="1dip" />
+ <ScrollView
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <ImageView
+ android:id="@+id/display"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/benchmark"
+ android:onClick="benchmark"/>
+ <TextView
+ android:id="@+id/benchmarkText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="8pt"
+ android:text="@string/saturation"/>
+ </LinearLayout>
+ <Spinner
+ android:id="@+id/filterselection"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"/>
+ <TextView
+ android:id="@+id/slider1Text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="8pt"
+ android:layout_marginLeft="10sp"
+ android:layout_marginTop="15sp"
+ android:text="@string/saturation"/>
+ <SeekBar
+ android:id="@+id/slider1"
+ android:layout_marginLeft="10sp"
+ android:layout_marginRight="10sp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+ <TextView
+ android:id="@+id/slider2Text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="8pt"
+ android:layout_marginLeft="10sp"
+ android:layout_marginTop="15sp"
+ android:text="@string/gamma"/>
+ <SeekBar
+ android:id="@+id/slider2"
+ android:layout_marginLeft="10sp"
+ android:layout_marginRight="10sp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+ <TextView
+ android:id="@+id/slider3Text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10sp"
+ android:layout_marginTop="15sp"
+ android:textSize="8pt"
+ android:text="@string/out_white"/>
+ <SeekBar
+ android:id="@+id/slider3"
+ android:layout_marginLeft="10sp"
+ android:layout_marginRight="10sp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+ <TextView
+ android:id="@+id/slider4Text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="8pt"
+ android:layout_marginLeft="10sp"
+ android:layout_marginTop="15sp"
+ android:text="@string/in_white"/>
+ <SeekBar
+ android:id="@+id/slider4"
+ android:layout_marginLeft="10sp"
+ android:layout_marginRight="10sp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+ <TextView
+ android:id="@+id/slider5Text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="8pt"
+ android:layout_marginLeft="10sp"
+ android:layout_marginTop="15sp"
+ android:text="@string/in_white"/>
+ <SeekBar
+ android:id="@+id/slider5"
+ android:layout_marginLeft="10sp"
+ android:layout_marginRight="10sp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+ </LinearLayout>
+ </ScrollView>
+</LinearLayout>
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/res/layout/spinner_layout.xml b/tests/RenderScriptTests/ImageProcessing2/res/layout/spinner_layout.xml
new file mode 100644
index 0000000..8196bbf
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/res/layout/spinner_layout.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:padding="10dp"
+ android:textSize="16sp"
+/>
diff --git a/tests/RenderScriptTests/ImageProcessing2/res/values/strings.xml b/tests/RenderScriptTests/ImageProcessing2/res/values/strings.xml
new file mode 100644
index 0000000..cc5cc4d
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/res/values/strings.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- General -->
+ <skip />
+ <!--slider label -->
+ <string name="blur_description">Blur Radius</string>
+ <string name="in_white">In White</string>
+ <string name="out_white">Out White</string>
+ <string name="in_black">In Black</string>
+ <string name="out_black">Out Black</string>
+ <string name="gamma">Gamma</string>
+ <string name="saturation">Saturation</string>
+ <string name="benchmark">Benchmark</string>
+
+</resources>
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Blur25.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Blur25.java
new file mode 100644
index 0000000..be87716
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Blur25.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rs.image2;
+
+import java.lang.Math;
+
+import android.support.v8.renderscript.*;
+import android.util.Log;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+public class Blur25 extends TestBase {
+ private int MAX_RADIUS = 25;
+ private ScriptC_threshold mScript;
+ private ScriptC_vertical_blur mScriptVBlur;
+ private ScriptC_horizontal_blur mScriptHBlur;
+ private int mRadius = MAX_RADIUS;
+ private float mSaturation = 1.0f;
+ private Allocation mScratchPixelsAllocation1;
+ private Allocation mScratchPixelsAllocation2;
+
+
+ public boolean onBar1Setup(SeekBar b, TextView t) {
+ t.setText("Radius");
+ b.setProgress(100);
+ return true;
+ }
+ public boolean onBar2Setup(SeekBar b, TextView t) {
+ b.setProgress(50);
+ t.setText("Saturation");
+ return true;
+ }
+
+
+ public void onBar1Changed(int progress) {
+ float fRadius = progress / 100.0f;
+ fRadius *= (float)(MAX_RADIUS);
+ mRadius = (int)fRadius;
+ mScript.set_radius(mRadius);
+ }
+ public void onBar2Changed(int progress) {
+ mSaturation = (float)progress / 50.0f;
+ mScriptVBlur.invoke_setSaturation(mSaturation);
+ }
+
+
+ public void createTest(android.content.res.Resources res) {
+ int width = mInPixelsAllocation.getType().getX();
+ int height = mInPixelsAllocation.getType().getY();
+
+ Type.Builder tb = new Type.Builder(mRS, Element.F32_4(mRS));
+ tb.setX(width);
+ tb.setY(height);
+ mScratchPixelsAllocation1 = Allocation.createTyped(mRS, tb.create());
+ mScratchPixelsAllocation2 = Allocation.createTyped(mRS, tb.create());
+
+ mScriptVBlur = new ScriptC_vertical_blur(mRS, res, R.raw.vertical_blur);
+ mScriptHBlur = new ScriptC_horizontal_blur(mRS, res, R.raw.horizontal_blur);
+
+ mScript = new ScriptC_threshold(mRS, res, R.raw.threshold);
+ mScript.set_width(width);
+ mScript.set_height(height);
+ mScript.set_radius(mRadius);
+
+ mScriptVBlur.invoke_setSaturation(mSaturation);
+
+ mScript.bind_InPixel(mInPixelsAllocation);
+ mScript.bind_OutPixel(mOutPixelsAllocation);
+ mScript.bind_ScratchPixel1(mScratchPixelsAllocation1);
+ mScript.bind_ScratchPixel2(mScratchPixelsAllocation2);
+
+ mScript.set_vBlurScript(mScriptVBlur);
+ mScript.set_hBlurScript(mScriptHBlur);
+ }
+
+ public void runTest() {
+ mScript.invoke_filter();
+ }
+
+ public void setupBenchmark() {
+ mScript.set_radius(MAX_RADIUS);
+ }
+
+ public void exitBenchmark() {
+ mScript.set_radius(mRadius);
+ }
+}
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Fisheye.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Fisheye.java
new file mode 100644
index 0000000..995cf9d
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Fisheye.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rs.image2;
+
+import android.support.v8.renderscript.*;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+public class Fisheye extends TestBase {
+ private ScriptC_fisheye_full mScript_full = null;
+ private ScriptC_fisheye_relaxed mScript_relaxed = null;
+ private final boolean relaxed;
+ private float center_x = 0.5f;
+ private float center_y = 0.5f;
+ private float scale = 0.5f;
+
+ public Fisheye(boolean relaxed) {
+ this.relaxed = relaxed;
+ }
+
+ public boolean onBar1Setup(SeekBar b, TextView t) {
+ t.setText("Scale");
+ b.setMax(100);
+ b.setProgress(25);
+ return true;
+ }
+ public boolean onBar2Setup(SeekBar b, TextView t) {
+ t.setText("Shift center X");
+ b.setMax(100);
+ b.setProgress(50);
+ return true;
+ }
+ public boolean onBar3Setup(SeekBar b, TextView t) {
+ t.setText("Shift center Y");
+ b.setMax(100);
+ b.setProgress(50);
+ return true;
+ }
+
+ public void onBar1Changed(int progress) {
+ scale = progress / 50.0f;
+ do_init();
+ }
+ public void onBar2Changed(int progress) {
+ center_x = progress / 100.0f;
+ do_init();
+ }
+ public void onBar3Changed(int progress) {
+ center_y = progress / 100.0f;
+ do_init();
+ }
+
+ private void do_init() {
+ if (relaxed)
+ mScript_relaxed.invoke_init_filter(
+ mInPixelsAllocation.getType().getX(),
+ mInPixelsAllocation.getType().getY(), center_x, center_y,
+ scale);
+ else
+ mScript_full.invoke_init_filter(
+ mInPixelsAllocation.getType().getX(),
+ mInPixelsAllocation.getType().getY(), center_x, center_y,
+ scale);
+ }
+
+ public void createTest(android.content.res.Resources res) {
+ if (relaxed) {
+ mScript_relaxed = new ScriptC_fisheye_relaxed(mRS, res,
+ R.raw.fisheye_relaxed);
+ mScript_relaxed.set_in_alloc(mInPixelsAllocation);
+ mScript_relaxed.set_sampler(Sampler.CLAMP_LINEAR(mRS));
+ } else {
+ mScript_full = new ScriptC_fisheye_full(mRS, res,
+ R.raw.fisheye_full);
+ mScript_full.set_in_alloc(mInPixelsAllocation);
+ mScript_full.set_sampler(Sampler.CLAMP_LINEAR(mRS));
+ }
+ do_init();
+ }
+
+ public void runTest() {
+ if (relaxed)
+ mScript_relaxed.forEach_root(mOutPixelsAllocation);
+ else
+ mScript_full.forEach_root(mOutPixelsAllocation);
+ }
+
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Grain.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Grain.java
new file mode 100644
index 0000000..e00edd7
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Grain.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rs.image2;
+
+import java.lang.Math;
+
+import android.support.v8.renderscript.*;
+import android.util.Log;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+public class Grain extends TestBase {
+ private ScriptC_grain mScript;
+ private Allocation mNoise;
+ private Allocation mNoise2;
+
+
+ public boolean onBar1Setup(SeekBar b, TextView t) {
+ t.setText("Strength");
+ b.setProgress(50);
+ return true;
+ }
+
+ public void onBar1Changed(int progress) {
+ float s = progress / 100.0f;
+ mScript.set_gNoiseStrength(s);
+ }
+
+ public void createTest(android.content.res.Resources res) {
+ int width = mInPixelsAllocation.getType().getX();
+ int height = mInPixelsAllocation.getType().getY();
+
+ Type.Builder tb = new Type.Builder(mRS, Element.U8(mRS));
+ tb.setX(width);
+ tb.setY(height);
+ mNoise = Allocation.createTyped(mRS, tb.create());
+ mNoise2 = Allocation.createTyped(mRS, tb.create());
+
+ mScript = new ScriptC_grain(mRS, res, R.raw.grain);
+ mScript.set_gWidth(width);
+ mScript.set_gHeight(height);
+ mScript.set_gNoiseStrength(0.5f);
+ mScript.set_gBlendSource(mNoise);
+ mScript.set_gNoise(mNoise2);
+ }
+
+ public void runTest() {
+ mScript.forEach_genRand(mNoise);
+ mScript.forEach_blend9(mNoise2);
+ mScript.forEach_root(mInPixelsAllocation, mOutPixelsAllocation);
+ }
+
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Greyscale.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Greyscale.java
new file mode 100644
index 0000000..2d85ae7
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Greyscale.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rs.image2;
+
+import java.lang.Math;
+
+import android.support.v8.renderscript.*;
+import android.util.Log;
+
+public class Greyscale extends TestBase {
+ private ScriptC_greyscale mScript;
+
+ public void createTest(android.content.res.Resources res) {
+ mScript = new ScriptC_greyscale(mRS, res, R.raw.greyscale);
+ }
+
+ public void runTest() {
+ mScript.forEach_root(mInPixelsAllocation, mOutPixelsAllocation);
+ }
+
+}
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/GroupTest.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/GroupTest.java
new file mode 100644
index 0000000..b9fbb59
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/GroupTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rs.image2;
+
+import java.lang.Math;
+
+import android.support.v8.renderscript.*;
+import android.util.Log;
+
+public class GroupTest extends TestBase {
+ private ScriptC_convolve3x3 mConvolve;
+ private ScriptC_colormatrix mMatrix;
+
+ private Allocation mScratchPixelsAllocation1;
+ private ScriptGroup mGroup;
+
+ private int mWidth;
+ private int mHeight;
+ private boolean mUseNative;
+
+
+ public GroupTest(boolean useNative) {
+ mUseNative = useNative;
+ }
+
+ public void createTest(android.content.res.Resources res) {
+ mWidth = mInPixelsAllocation.getType().getX();
+ mHeight = mInPixelsAllocation.getType().getY();
+
+ mConvolve = new ScriptC_convolve3x3(mRS, res, R.raw.convolve3x3);
+ mMatrix = new ScriptC_colormatrix(mRS, res, R.raw.colormatrix);
+
+ float f[] = new float[9];
+ f[0] = 0.f; f[1] = -1.f; f[2] = 0.f;
+ f[3] = -1.f; f[4] = 5.f; f[5] = -1.f;
+ f[6] = 0.f; f[7] = -1.f; f[8] = 0.f;
+ mConvolve.set_gCoeffs(f);
+
+ Matrix4f m = new Matrix4f();
+ m.set(1, 0, 0.2f);
+ m.set(1, 1, 0.9f);
+ m.set(1, 2, 0.2f);
+ mMatrix.invoke_setMatrix(m);
+
+ Type.Builder tb = new Type.Builder(mRS, Element.U8_4(mRS));
+ tb.setX(mWidth);
+ tb.setY(mHeight);
+ Type connect = tb.create();
+
+ if (mUseNative) {
+ ScriptGroup.Builder b = new ScriptGroup.Builder(mRS);
+ b.addConnection(connect, mConvolve, mMatrix, null);
+ mGroup = b.create();
+
+ } else {
+ mScratchPixelsAllocation1 = Allocation.createTyped(mRS, connect);
+ }
+ }
+
+ public void runTest() {
+ mConvolve.set_gIn(mInPixelsAllocation);
+ mConvolve.set_gWidth(mWidth);
+ mConvolve.set_gHeight(mHeight);
+ if (mUseNative) {
+ mGroup.setOutput(mMatrix, mOutPixelsAllocation);
+ mGroup.execute();
+ } else {
+ mConvolve.forEach_root(mScratchPixelsAllocation1);
+ mMatrix.forEach_root(mScratchPixelsAllocation1, mOutPixelsAllocation);
+ }
+ }
+
+}
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ImageProcessingActivity2.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ImageProcessingActivity2.java
new file mode 100644
index 0000000..9b36da14
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ImageProcessingActivity2.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rs.image2;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.graphics.BitmapFactory;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.support.v8.renderscript.*;
+import android.view.SurfaceView;
+import android.view.SurfaceHolder;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.SeekBar;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.view.View;
+import android.util.Log;
+import java.lang.Math;
+
+public class ImageProcessingActivity2 extends Activity
+ implements SeekBar.OnSeekBarChangeListener {
+ private final String TAG = "Img";
+ Bitmap mBitmapIn;
+ Bitmap mBitmapOut;
+ String mTestNames[];
+
+ private SeekBar mBar1;
+ private SeekBar mBar2;
+ private SeekBar mBar3;
+ private SeekBar mBar4;
+ private SeekBar mBar5;
+ private TextView mText1;
+ private TextView mText2;
+ private TextView mText3;
+ private TextView mText4;
+ private TextView mText5;
+
+ private float mSaturation = 1.0f;
+
+ private TextView mBenchmarkResult;
+ private Spinner mTestSpinner;
+
+ private SurfaceView mSurfaceView;
+ private ImageView mDisplayView;
+
+ private boolean mDoingBenchmark;
+
+ private TestBase mTest;
+
+
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (fromUser) {
+
+ if (seekBar == mBar1) {
+ mTest.onBar1Changed(progress);
+ } else if (seekBar == mBar2) {
+ mTest.onBar2Changed(progress);
+ } else if (seekBar == mBar3) {
+ mTest.onBar3Changed(progress);
+ } else if (seekBar == mBar4) {
+ mTest.onBar4Changed(progress);
+ } else if (seekBar == mBar5) {
+ mTest.onBar5Changed(progress);
+ }
+
+ mTest.runTest();
+ mTest.updateBitmap(mBitmapOut);
+ mDisplayView.invalidate();
+ }
+ }
+
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+
+ void setupBars() {
+ mBar1.setVisibility(View.VISIBLE);
+ mText1.setVisibility(View.VISIBLE);
+ mTest.onBar1Setup(mBar1, mText1);
+
+ mBar2.setVisibility(View.VISIBLE);
+ mText2.setVisibility(View.VISIBLE);
+ mTest.onBar2Setup(mBar2, mText2);
+
+ mBar3.setVisibility(View.VISIBLE);
+ mText3.setVisibility(View.VISIBLE);
+ mTest.onBar3Setup(mBar3, mText3);
+
+ mBar4.setVisibility(View.VISIBLE);
+ mText4.setVisibility(View.VISIBLE);
+ mTest.onBar4Setup(mBar4, mText4);
+
+ mBar5.setVisibility(View.VISIBLE);
+ mText5.setVisibility(View.VISIBLE);
+ mTest.onBar5Setup(mBar5, mText5);
+ }
+
+
+ void changeTest(int testID) {
+ switch(testID) {
+ case 0:
+ mTest = new LevelsV4(false, false);
+ break;
+ case 1:
+ mTest = new LevelsV4(false, true);
+ break;
+ case 2:
+ mTest = new LevelsV4(true, false);
+ break;
+ case 3:
+ mTest = new LevelsV4(true, true);
+ break;
+ case 4:
+ mTest = new Blur25();
+ break;
+ case 5:
+ mTest = new Greyscale();
+ break;
+ case 6:
+ mTest = new Grain();
+ break;
+ case 7:
+ mTest = new Fisheye(false);
+ break;
+ case 8:
+ mTest = new Fisheye(true);
+ break;
+ case 9:
+ mTest = new Vignette(false);
+ break;
+ case 10:
+ mTest = new Vignette(true);
+ break;
+ case 11:
+ mTest = new GroupTest(false);
+ break;
+ case 12:
+ mTest = new GroupTest(true);
+ break;
+ }
+
+ mTest.createBaseTest(this, mBitmapIn);
+ setupBars();
+
+ mTest.runTest();
+ mTest.updateBitmap(mBitmapOut);
+ mDisplayView.invalidate();
+ mBenchmarkResult.setText("Result: not run");
+ }
+
+ void setupTests() {
+ mTestNames = new String[13];
+ mTestNames[0] = "Levels Vec3 Relaxed";
+ mTestNames[1] = "Levels Vec4 Relaxed";
+ mTestNames[2] = "Levels Vec3 Full";
+ mTestNames[3] = "Levels Vec4 Full";
+ mTestNames[4] = "Blur radius 25";
+ mTestNames[5] = "Greyscale";
+ mTestNames[6] = "Grain";
+ mTestNames[7] = "Fisheye Full";
+ mTestNames[8] = "Fisheye Relaxed";
+ mTestNames[9] = "Vignette Full";
+ mTestNames[10] = "Vignette Relaxed";
+ mTestNames[11] = "Group Test (emulated)";
+ mTestNames[12] = "Group Test (native)";
+ mTestSpinner.setAdapter(new ArrayAdapter<String>(
+ this, R.layout.spinner_layout, mTestNames));
+ }
+
+ private AdapterView.OnItemSelectedListener mTestSpinnerListener =
+ new AdapterView.OnItemSelectedListener() {
+ public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
+ changeTest(pos);
+ }
+
+ public void onNothingSelected(AdapterView parent) {
+
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ mBitmapIn = loadBitmap(R.drawable.city);
+ mBitmapOut = loadBitmap(R.drawable.city);
+
+ mSurfaceView = (SurfaceView) findViewById(R.id.surface);
+
+ mDisplayView = (ImageView) findViewById(R.id.display);
+ mDisplayView.setImageBitmap(mBitmapOut);
+
+ mBar1 = (SeekBar) findViewById(R.id.slider1);
+ mBar2 = (SeekBar) findViewById(R.id.slider2);
+ mBar3 = (SeekBar) findViewById(R.id.slider3);
+ mBar4 = (SeekBar) findViewById(R.id.slider4);
+ mBar5 = (SeekBar) findViewById(R.id.slider5);
+
+ mBar1.setOnSeekBarChangeListener(this);
+ mBar2.setOnSeekBarChangeListener(this);
+ mBar3.setOnSeekBarChangeListener(this);
+ mBar4.setOnSeekBarChangeListener(this);
+ mBar5.setOnSeekBarChangeListener(this);
+
+ mText1 = (TextView) findViewById(R.id.slider1Text);
+ mText2 = (TextView) findViewById(R.id.slider2Text);
+ mText3 = (TextView) findViewById(R.id.slider3Text);
+ mText4 = (TextView) findViewById(R.id.slider4Text);
+ mText5 = (TextView) findViewById(R.id.slider5Text);
+
+ mTestSpinner = (Spinner) findViewById(R.id.filterselection);
+ mTestSpinner.setOnItemSelectedListener(mTestSpinnerListener);
+
+ mBenchmarkResult = (TextView) findViewById(R.id.benchmarkText);
+ mBenchmarkResult.setText("Result: not run");
+
+ setupTests();
+ changeTest(0);
+ }
+
+
+ private Bitmap loadBitmap(int resource) {
+ final BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inPreferredConfig = Bitmap.Config.ARGB_8888;
+ return copyBitmap(BitmapFactory.decodeResource(getResources(), resource, options));
+ }
+
+ private static Bitmap copyBitmap(Bitmap source) {
+ Bitmap b = Bitmap.createBitmap(source.getWidth(), source.getHeight(), source.getConfig());
+ Canvas c = new Canvas(b);
+ c.drawBitmap(source, 0, 0, null);
+ source.recycle();
+ return b;
+ }
+
+ // button hook
+ public void benchmark(View v) {
+ long t = getBenchmark();
+ //long javaTime = javaFilter();
+ //mBenchmarkResult.setText("RS: " + t + " ms Java: " + javaTime + " ms");
+ mBenchmarkResult.setText("Result: " + t + " ms");
+ }
+
+ // For benchmark test
+ public long getBenchmark() {
+ mDoingBenchmark = true;
+
+ mTest.setupBenchmark();
+ long result = 0;
+
+ Log.v(TAG, "Warming");
+ long t = java.lang.System.currentTimeMillis() + 2000;
+ do {
+ mTest.runTest();
+ mTest.finish();
+ } while (t > java.lang.System.currentTimeMillis());
+
+
+ Log.v(TAG, "Benchmarking");
+ t = java.lang.System.currentTimeMillis();
+ mTest.runTest();
+ mTest.finish();
+ t = java.lang.System.currentTimeMillis() - t;
+
+ Log.v(TAG, "getBenchmark: Renderscript frame time core ms " + t);
+ mTest.exitBenchmark();
+ mDoingBenchmark = false;
+
+ return t;
+ }
+}
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/LevelsV4.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/LevelsV4.java
new file mode 100644
index 0000000..fbe3727
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/LevelsV4.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rs.image2;
+
+import java.lang.Math;
+
+import android.support.v8.renderscript.*;
+import android.util.Log;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+
+public class LevelsV4 extends TestBase {
+ private ScriptC_levels_relaxed mScriptR;
+ private ScriptC_levels_full mScriptF;
+ private float mInBlack = 0.0f;
+ private float mOutBlack = 0.0f;
+ private float mInWhite = 255.0f;
+ private float mOutWhite = 255.0f;
+ private float mSaturation = 1.0f;
+
+ Matrix3f satMatrix = new Matrix3f();
+ float mInWMinInB;
+ float mOutWMinOutB;
+ float mOverInWMinInB;
+
+ boolean mUseFull;
+ boolean mUseV4;
+
+ LevelsV4(boolean useFull, boolean useV4) {
+ mUseFull = useFull;
+ mUseV4 = useV4;
+ }
+
+
+ private void setLevels() {
+ mInWMinInB = mInWhite - mInBlack;
+ mOutWMinOutB = mOutWhite - mOutBlack;
+ mOverInWMinInB = 1.f / mInWMinInB;
+
+ mScriptR.set_inBlack(mInBlack);
+ mScriptR.set_outBlack(mOutBlack);
+ mScriptR.set_inWMinInB(mInWMinInB);
+ mScriptR.set_outWMinOutB(mOutWMinOutB);
+ mScriptR.set_overInWMinInB(mOverInWMinInB);
+ mScriptF.set_inBlack(mInBlack);
+ mScriptF.set_outBlack(mOutBlack);
+ mScriptF.set_inWMinInB(mInWMinInB);
+ mScriptF.set_outWMinOutB(mOutWMinOutB);
+ mScriptF.set_overInWMinInB(mOverInWMinInB);
+ }
+
+ private void setSaturation() {
+ float rWeight = 0.299f;
+ float gWeight = 0.587f;
+ float bWeight = 0.114f;
+ float oneMinusS = 1.0f - mSaturation;
+
+ satMatrix.set(0, 0, oneMinusS * rWeight + mSaturation);
+ satMatrix.set(0, 1, oneMinusS * rWeight);
+ satMatrix.set(0, 2, oneMinusS * rWeight);
+ satMatrix.set(1, 0, oneMinusS * gWeight);
+ satMatrix.set(1, 1, oneMinusS * gWeight + mSaturation);
+ satMatrix.set(1, 2, oneMinusS * gWeight);
+ satMatrix.set(2, 0, oneMinusS * bWeight);
+ satMatrix.set(2, 1, oneMinusS * bWeight);
+ satMatrix.set(2, 2, oneMinusS * bWeight + mSaturation);
+ mScriptR.set_colorMat(satMatrix);
+ mScriptF.set_colorMat(satMatrix);
+ }
+
+ public boolean onBar1Setup(SeekBar b, TextView t) {
+ b.setProgress(50);
+ t.setText("Saturation");
+ return true;
+ }
+ public boolean onBar2Setup(SeekBar b, TextView t) {
+ b.setMax(128);
+ b.setProgress(0);
+ t.setText("In Black");
+ return true;
+ }
+ public boolean onBar3Setup(SeekBar b, TextView t) {
+ b.setMax(128);
+ b.setProgress(0);
+ t.setText("Out Black");
+ return true;
+ }
+ public boolean onBar4Setup(SeekBar b, TextView t) {
+ b.setMax(128);
+ b.setProgress(128);
+ t.setText("Out White");
+ return true;
+ }
+ public boolean onBar5Setup(SeekBar b, TextView t) {
+ b.setMax(128);
+ b.setProgress(128);
+ t.setText("Out White");
+ return true;
+ }
+
+ public void onBar1Changed(int progress) {
+ mSaturation = (float)progress / 50.0f;
+ setSaturation();
+ }
+ public void onBar2Changed(int progress) {
+ mInBlack = (float)progress;
+ setLevels();
+ }
+ public void onBar3Changed(int progress) {
+ mOutBlack = (float)progress;
+ setLevels();
+ }
+ public void onBar4Changed(int progress) {
+ mInWhite = (float)progress + 127.0f;
+ setLevels();
+ }
+ public void onBar5Changed(int progress) {
+ mOutWhite = (float)progress + 127.0f;
+ setLevels();
+ }
+
+ public void createTest(android.content.res.Resources res) {
+ mScriptR = new ScriptC_levels_relaxed(mRS, res, R.raw.levels_relaxed);
+ mScriptF = new ScriptC_levels_full(mRS, res, R.raw.levels_full);
+ setSaturation();
+ setLevels();
+ }
+
+ public void runTest() {
+ if (mUseFull) {
+ if (mUseV4) {
+ mScriptF.forEach_root4(mInPixelsAllocation, mOutPixelsAllocation);
+ } else {
+ mScriptF.forEach_root(mInPixelsAllocation, mOutPixelsAllocation);
+ }
+ } else {
+ if (mUseV4) {
+ mScriptR.forEach_root4(mInPixelsAllocation, mOutPixelsAllocation);
+ } else {
+ mScriptR.forEach_root(mInPixelsAllocation, mOutPixelsAllocation);
+ }
+ }
+ }
+
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/TestBase.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/TestBase.java
new file mode 100644
index 0000000..35170af
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/TestBase.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rs.image2;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.graphics.BitmapFactory;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.support.v8.renderscript.*;
+import android.view.SurfaceView;
+import android.view.SurfaceHolder;
+import android.widget.ImageView;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import android.view.View;
+import android.util.Log;
+import java.lang.Math;
+
+public class TestBase {
+ protected final String TAG = "Img";
+
+ protected RenderScript mRS;
+ protected Allocation mInPixelsAllocation;
+ protected Allocation mOutPixelsAllocation;
+
+ // Override to use UI elements
+ public void onBar1Changed(int progress) {
+ }
+ public void onBar2Changed(int progress) {
+ }
+ public void onBar3Changed(int progress) {
+ }
+ public void onBar4Changed(int progress) {
+ }
+ public void onBar5Changed(int progress) {
+ }
+
+ // Override to use UI elements
+ // Unused bars will be hidden.
+ public boolean onBar1Setup(SeekBar b, TextView t) {
+ b.setVisibility(View.INVISIBLE);
+ t.setVisibility(View.INVISIBLE);
+ return false;
+ }
+ public boolean onBar2Setup(SeekBar b, TextView t) {
+ b.setVisibility(View.INVISIBLE);
+ t.setVisibility(View.INVISIBLE);
+ return false;
+ }
+ public boolean onBar3Setup(SeekBar b, TextView t) {
+ b.setVisibility(View.INVISIBLE);
+ t.setVisibility(View.INVISIBLE);
+ return false;
+ }
+ public boolean onBar4Setup(SeekBar b, TextView t) {
+ b.setVisibility(View.INVISIBLE);
+ t.setVisibility(View.INVISIBLE);
+ return false;
+ }
+ public boolean onBar5Setup(SeekBar b, TextView t) {
+ b.setVisibility(View.INVISIBLE);
+ t.setVisibility(View.INVISIBLE);
+ return false;
+ }
+
+ public final void createBaseTest(ImageProcessingActivity2 act, Bitmap b) {
+ mRS = RenderScript.create(act);
+ mInPixelsAllocation = Allocation.createFromBitmap(mRS, b,
+ Allocation.MipmapControl.MIPMAP_NONE,
+ Allocation.USAGE_SCRIPT);
+ mOutPixelsAllocation = Allocation.createFromBitmap(mRS, b,
+ Allocation.MipmapControl.MIPMAP_NONE,
+ Allocation.USAGE_SCRIPT);
+ createTest(act.getResources());
+ }
+
+ // Must override
+ public void createTest(android.content.res.Resources res) {
+ android.util.Log.e("img", "implement createTest");
+ }
+
+ // Must override
+ public void runTest() {
+ }
+
+ public void finish() {
+ mRS.finish();
+ }
+
+ public void updateBitmap(Bitmap b) {
+ mOutPixelsAllocation.copyTo(b);
+ }
+
+ // Override to configure specific benchmark config.
+ public void setupBenchmark() {
+ }
+
+ // Override to reset after benchmark.
+ public void exitBenchmark() {
+ }
+}
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Vignette.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Vignette.java
new file mode 100644
index 0000000..fc69eba
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Vignette.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rs.image2;
+
+import android.support.v8.renderscript.*;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+public class Vignette extends TestBase {
+ private ScriptC_vignette_full mScript_full = null;
+ private ScriptC_vignette_relaxed mScript_relaxed = null;
+ private final boolean relaxed;
+ private float center_x = 0.5f;
+ private float center_y = 0.5f;
+ private float scale = 0.5f;
+ private float shade = 0.5f;
+ private float slope = 20.0f;
+
+ public Vignette(boolean relaxed) {
+ this.relaxed = relaxed;
+ }
+
+ public boolean onBar1Setup(SeekBar b, TextView t) {
+ t.setText("Scale");
+ b.setMax(100);
+ b.setProgress(25);
+ return true;
+ }
+ public boolean onBar2Setup(SeekBar b, TextView t) {
+ t.setText("Shade");
+ b.setMax(100);
+ b.setProgress(50);
+ return true;
+ }
+ public boolean onBar3Setup(SeekBar b, TextView t) {
+ t.setText("Slope");
+ b.setMax(100);
+ b.setProgress(20);
+ return true;
+ }
+ public boolean onBar4Setup(SeekBar b, TextView t) {
+ t.setText("Shift center X");
+ b.setMax(100);
+ b.setProgress(50);
+ return true;
+ }
+ public boolean onBar5Setup(SeekBar b, TextView t) {
+ t.setText("Shift center Y");
+ b.setMax(100);
+ b.setProgress(50);
+ return true;
+ }
+
+ public void onBar1Changed(int progress) {
+ scale = progress / 50.0f;
+ do_init();
+ }
+ public void onBar2Changed(int progress) {
+ shade = progress / 100.0f;
+ do_init();
+ }
+ public void onBar3Changed(int progress) {
+ slope = (float)progress;
+ do_init();
+ }
+ public void onBar4Changed(int progress) {
+ center_x = progress / 100.0f;
+ do_init();
+ }
+ public void onBar5Changed(int progress) {
+ center_y = progress / 100.0f;
+ do_init();
+ }
+
+ private void do_init() {
+ if (relaxed)
+ mScript_relaxed.invoke_init_vignette(
+ mInPixelsAllocation.getType().getX(),
+ mInPixelsAllocation.getType().getY(), center_x, center_y,
+ scale, shade, slope);
+ else
+ mScript_full.invoke_init_vignette(
+ mInPixelsAllocation.getType().getX(),
+ mInPixelsAllocation.getType().getY(), center_x, center_y,
+ scale, shade, slope);
+ }
+
+ public void createTest(android.content.res.Resources res) {
+ if (relaxed) {
+ mScript_relaxed = new ScriptC_vignette_relaxed(mRS, res,
+ R.raw.vignette_relaxed);
+ } else {
+ mScript_full = new ScriptC_vignette_full(mRS, res,
+ R.raw.vignette_full);
+ }
+ do_init();
+ }
+
+ public void runTest() {
+ if (relaxed)
+ mScript_relaxed.forEach_root(mInPixelsAllocation, mOutPixelsAllocation);
+ else
+ mScript_full.forEach_root(mInPixelsAllocation, mOutPixelsAllocation);
+ }
+
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/colormatrix.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/colormatrix.rs
new file mode 100644
index 0000000..e93bef3
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/colormatrix.rs
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.image2)
+#pragma rs_fp_relaxed
+
+
+static rs_matrix4x4 Mat;
+
+void init() {
+ rsMatrixLoadIdentity(&Mat);
+}
+
+void setMatrix(rs_matrix4x4 m) {
+ Mat = m;
+}
+
+void root(const uchar4 *in, uchar4 *out) {
+ float4 f = convert_float4(*in);
+ f = rsMatrixMultiply(&Mat, f);
+ f = clamp(f, 0.f, 255.f);
+ *out = convert_uchar4(f);
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/convolve3x3.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/convolve3x3.rs
new file mode 100644
index 0000000..b55190c
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/convolve3x3.rs
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.image2)
+#pragma rs_fp_relaxed
+
+int32_t gWidth;
+int32_t gHeight;
+rs_allocation gIn;
+
+float gCoeffs[9];
+
+void root(uchar4 *out, uint32_t x, uint32_t y) {
+ uint32_t x1 = min((int32_t)x+1, gWidth);
+ uint32_t x2 = max((int32_t)x-1, 0);
+ uint32_t y1 = min((int32_t)y+1, gHeight);
+ uint32_t y2 = max((int32_t)y-1, 0);
+
+ float4 p00 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x1, y1))[0]);
+ float4 p01 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x, y1))[0]);
+ float4 p02 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x2, y1))[0]);
+ float4 p10 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x1, y))[0]);
+ float4 p11 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x, y))[0]);
+ float4 p12 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x2, y))[0]);
+ float4 p20 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x1, y2))[0]);
+ float4 p21 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x, y2))[0]);
+ float4 p22 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x2, y2))[0]);
+ p00 *= gCoeffs[0];
+ p01 *= gCoeffs[1];
+ p02 *= gCoeffs[2];
+ p10 *= gCoeffs[3];
+ p11 *= gCoeffs[4];
+ p12 *= gCoeffs[5];
+ p20 *= gCoeffs[6];
+ p21 *= gCoeffs[7];
+ p22 *= gCoeffs[8];
+
+ p00 += p01;
+ p02 += p10;
+ p11 += p12;
+ p20 += p21;
+
+ p22 += p00;
+ p02 += p11;
+
+ p20 += p22;
+ p20 += p02;
+
+ p20 = clamp(p20, 0.f, 255.f);
+ *out = convert_uchar4(p20);
+}
+
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye.rsh b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye.rsh
new file mode 100644
index 0000000..4dcfc1d
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye.rsh
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+rs_allocation in_alloc;
+rs_sampler sampler;
+
+static float2 center, dimensions;
+static float2 scale;
+static float alpha;
+static float radius2;
+static float factor;
+
+void init_filter(uint32_t dim_x, uint32_t dim_y, float focus_x, float focus_y, float k) {
+ center.x = focus_x;
+ center.y = focus_y;
+ dimensions.x = (float)dim_x;
+ dimensions.y = (float)dim_y;
+
+ alpha = k * 2.0 + 0.75;
+ float bound2 = 0.25;
+ if (dim_x > dim_y) {
+ scale.x = 1.0;
+ scale.y = dimensions.y / dimensions.x;
+ bound2 *= (scale.y*scale.y + 1);
+ } else {
+ scale.x = dimensions.x / dimensions.y;
+ scale.y = 1.0;
+ bound2 *= (scale.x*scale.x + 1);
+ }
+ const float bound = sqrt(bound2);
+ const float radius = 1.15 * bound;
+ radius2 = radius*radius;
+ const float max_radian = 0.5f * M_PI - atan(alpha / bound * sqrt(radius2 - bound2));
+ factor = bound / max_radian;
+}
+
+void root(uchar4 *out, uint32_t x, uint32_t y) {
+ // Convert x and y to floating point coordinates with center as origin
+ float2 coord;
+ coord.x = (float)x / dimensions.x;
+ coord.y = (float)y / dimensions.y;
+ coord -= center;
+ const float dist = length(scale * coord);
+ const float radian = M_PI_2 - atan((alpha * sqrt(radius2 - dist * dist)) / dist);
+ const float scalar = radian * factor / dist;
+ const float2 new_coord = coord * scalar + center;
+ const float4 fout = rsSample(in_alloc, sampler, new_coord);
+ *out = rsPackColorTo8888(fout);
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_full.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_full.rs
new file mode 100644
index 0000000..e42df13
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_full.rs
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.image2)
+
+#include "fisheye.rsh"
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_relaxed.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_relaxed.rs
new file mode 100644
index 0000000..990310b
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_relaxed.rs
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.image2)
+#pragma rs_fp_relaxed
+
+#include "fisheye.rsh"
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/grain.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/grain.rs
new file mode 100644
index 0000000..75f4021
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/grain.rs
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.image2)
+#pragma rs_fp_relaxed
+
+void genRand(uchar *out) {
+ *out = (uchar)rsRand(0xff);
+}
+
+/*
+ * Convolution matrix of distance 2 with fixed point of 'kShiftBits' bits
+ * shifted. Thus the sum of this matrix should be 'kShiftValue'. Entries of
+ * small values are not calculated to gain efficiency.
+ * The order ot pixels represented in this matrix is:
+ * 1 2 3
+ * 4 0 5
+ * 6 7 8
+ * and the matrix should be: {230, 56, 114, 56, 114, 114, 56, 114, 56}.
+ * However, since most of the valus are identical, we only use the first three
+ * entries and the entries corresponding to the pixels is:
+ * 1 2 1
+ * 2 0 2
+ * 1 2 1
+ */
+
+int32_t gWidth;
+int32_t gHeight;
+
+rs_allocation gBlendSource;
+void blend9(uchar *out, uint32_t x, uint32_t y) {
+ uint32_t x1 = min(x+1, (uint32_t)gWidth);
+ uint32_t x2 = max(x-1, (uint32_t)0);
+ uint32_t y1 = min(y+1, (uint32_t)gHeight);
+ uint32_t y2 = max(y-1, (uint32_t)0);
+
+ uint p00 = 56 * ((uchar *)rsGetElementAt(gBlendSource, x1, y1))[0];
+ uint p01 = 114 * ((uchar *)rsGetElementAt(gBlendSource, x, y1))[0];
+ uint p02 = 56 * ((uchar *)rsGetElementAt(gBlendSource, x2, y1))[0];
+ uint p10 = 114 * ((uchar *)rsGetElementAt(gBlendSource, x1, y))[0];
+ uint p11 = 230 * ((uchar *)rsGetElementAt(gBlendSource, x, y))[0];
+ uint p12 = 114 * ((uchar *)rsGetElementAt(gBlendSource, x2, y))[0];
+ uint p20 = 56 * ((uchar *)rsGetElementAt(gBlendSource, x1, y2))[0];
+ uint p21 = 114 * ((uchar *)rsGetElementAt(gBlendSource, x, y2))[0];
+ uint p22 = 56 * ((uchar *)rsGetElementAt(gBlendSource, x2, y2))[0];
+
+ p00 += p01;
+ p02 += p10;
+ p11 += p12;
+ p20 += p21;
+
+ p22 += p00;
+ p02 += p11;
+
+ p20 += p22;
+ p20 += p02;
+
+ *out = (uchar)(p20 >> 10);
+}
+
+float gNoiseStrength;
+
+rs_allocation gNoise;
+void root(const uchar4 *in, uchar4 *out, uint32_t x, uint32_t y) {
+ float4 ip = convert_float4(*in);
+ float pnoise = (float) ((uchar *)rsGetElementAt(gNoise, x, y))[0];
+
+ float energy_level = ip.r + ip.g + ip.b;
+ float energy_mask = (28.f - sqrt(energy_level)) * 0.03571f;
+ pnoise = (pnoise - 128.f) * energy_mask;
+
+ ip += pnoise * gNoiseStrength;
+ ip = clamp(ip, 0.f, 255.f);
+
+ uchar4 p = convert_uchar4(ip);
+ p.a = 0xff;
+ *out = p;
+}
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/greyscale.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/greyscale.rs
new file mode 100644
index 0000000..b5abf3f0
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/greyscale.rs
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.image2)
+#pragma rs_fp_relaxed
+
+const static float3 gMonoMult = {0.299f, 0.587f, 0.114f};
+
+void root(const uchar4 *v_in, uchar4 *v_out) {
+ float4 f4 = rsUnpackColor8888(*v_in);
+
+ float3 mono = dot(f4.rgb, gMonoMult);
+ *v_out = rsPackColorTo8888(mono);
+}
+
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/horizontal_blur.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/horizontal_blur.rs
new file mode 100644
index 0000000..ee83496
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/horizontal_blur.rs
@@ -0,0 +1,28 @@
+#pragma version(1)
+#pragma rs_fp_relaxed
+
+#include "ip.rsh"
+
+void root(float4 *out, const void *usrData, uint32_t x, uint32_t y) {
+ const FilterStruct *fs = (const FilterStruct *)usrData;
+ float3 blurredPixel = 0;
+ const float *gPtr = fs->gaussian;
+ if ((x > fs->radius) && (x < (fs->width - fs->radius))) {
+ for (int r = -fs->radius; r <= fs->radius; r ++) {
+ const float4 *i = (const float4 *)rsGetElementAt(fs->ain, x + r, y);
+ blurredPixel += i->xyz * gPtr[0];
+ gPtr++;
+ }
+ } else {
+ for (int r = -fs->radius; r <= fs->radius; r ++) {
+ // Stepping left and right away from the pixel
+ int validX = rsClamp((int)x + r, (int)0, (int)(fs->width - 1));
+ const float4 *i = (const float4 *)rsGetElementAt(fs->ain, validX, y);
+ blurredPixel += i->xyz * gPtr[0];
+ gPtr++;
+ }
+ }
+
+ out->xyz = blurredPixel;
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ip.rsh b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ip.rsh
new file mode 100644
index 0000000..0cdf9e1
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ip.rsh
@@ -0,0 +1,15 @@
+#pragma rs java_package_name(com.android.rs.image2)
+
+#define MAX_RADIUS 25
+
+typedef struct FilterStruct_s {
+ rs_allocation ain;
+
+ float *gaussian; //[MAX_RADIUS * 2 + 1];
+ int height;
+ int width;
+ int radius;
+
+} FilterStruct;
+
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels.rsh b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels.rsh
new file mode 100644
index 0000000..7c5d930
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels.rsh
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+float inBlack;
+float outBlack;
+float inWMinInB;
+float outWMinOutB;
+float overInWMinInB;
+rs_matrix3x3 colorMat;
+
+void root(const uchar4 *in, uchar4 *out, uint32_t x, uint32_t y) {
+ float3 pixel = convert_float4(in[0]).rgb;
+ pixel = rsMatrixMultiply(&colorMat, pixel);
+ pixel = clamp(pixel, 0.f, 255.f);
+ pixel = (pixel - inBlack) * overInWMinInB;
+ pixel = pixel * outWMinOutB + outBlack;
+ pixel = clamp(pixel, 0.f, 255.f);
+ out->xyz = convert_uchar3(pixel);
+ out->w = 0xff;
+}
+
+void root4(const uchar4 *in, uchar4 *out, uint32_t x, uint32_t y) {
+ float4 pixel = convert_float4(in[0]);
+ pixel.rgb = rsMatrixMultiply(&colorMat, pixel.rgb);
+ pixel = clamp(pixel, 0.f, 255.f);
+ pixel = (pixel - inBlack) * overInWMinInB;
+ pixel = pixel * outWMinOutB + outBlack;
+ pixel = clamp(pixel, 0.f, 255.f);
+ out->xyzw = convert_uchar4(pixel);
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_full.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_full.rs
new file mode 100644
index 0000000..a4aa388
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_full.rs
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.image2)
+
+#include "levels.rsh"
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_relaxed.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_relaxed.rs
new file mode 100644
index 0000000..ffdcfe3
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_relaxed.rs
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.image2)
+#pragma rs_fp_relaxed
+
+#include "levels.rsh"
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/threshold.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/threshold.rs
new file mode 100644
index 0000000..77cd5be
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/threshold.rs
@@ -0,0 +1,93 @@
+#pragma version(1)
+
+#include "ip.rsh"
+
+int height;
+int width;
+int radius;
+
+uchar4 * InPixel;
+uchar4 * OutPixel;
+float4 * ScratchPixel1;
+float4 * ScratchPixel2;
+
+rs_script vBlurScript;
+rs_script hBlurScript;
+
+const int CMD_FINISHED = 1;
+
+// Store our coefficients here
+static float gaussian[MAX_RADIUS * 2 + 1];
+
+
+static void computeGaussianWeights() {
+ // Compute gaussian weights for the blur
+ // e is the euler's number
+ float e = 2.718281828459045f;
+ float pi = 3.1415926535897932f;
+ // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
+ // x is of the form [-radius .. 0 .. radius]
+ // and sigma varies with radius.
+ // Based on some experimental radius values and sigma's
+ // we approximately fit sigma = f(radius) as
+ // sigma = radius * 0.4 + 0.6
+ // The larger the radius gets, the more our gaussian blur
+ // will resemble a box blur since with large sigma
+ // the gaussian curve begins to lose its shape
+ float sigma = 0.4f * (float)radius + 0.6f;
+
+ // Now compute the coefficints
+ // We will store some redundant values to save some math during
+ // the blur calculations
+ // precompute some values
+ float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
+ float coeff2 = - 1.0f / (2.0f * sigma * sigma);
+
+ float normalizeFactor = 0.0f;
+ float floatR = 0.0f;
+ int r;
+ for (r = -radius; r <= radius; r ++) {
+ floatR = (float)r;
+ gaussian[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
+ normalizeFactor += gaussian[r + radius];
+ }
+
+ //Now we need to normalize the weights because all our coefficients need to add up to one
+ normalizeFactor = 1.0f / normalizeFactor;
+ for (r = -radius; r <= radius; r ++) {
+ floatR = (float)r;
+ gaussian[r + radius] *= normalizeFactor;
+ }
+}
+
+
+static void copyInput() {
+ rs_allocation ain;
+ ain = rsGetAllocation(InPixel);
+ uint32_t dimx = rsAllocationGetDimX(ain);
+ uint32_t dimy = rsAllocationGetDimY(ain);
+ for (uint32_t y = 0; y < dimy; y++) {
+ for (uint32_t x = 0; x < dimx; x++) {
+ ScratchPixel1[x + y * dimx] = convert_float4(InPixel[x + y * dimx]);
+ }
+ }
+}
+
+void filter() {
+ copyInput();
+ computeGaussianWeights();
+
+ FilterStruct fs;
+ fs.gaussian = gaussian;
+ fs.width = width;
+ fs.height = height;
+ fs.radius = radius;
+
+ fs.ain = rsGetAllocation(ScratchPixel1);
+ rsForEach(hBlurScript, fs.ain, rsGetAllocation(ScratchPixel2), &fs, sizeof(fs));
+
+ fs.ain = rsGetAllocation(ScratchPixel2);
+ rsForEach(vBlurScript, fs.ain, rsGetAllocation(OutPixel), &fs, sizeof(fs));
+ //rsSendToClientBlocking(CMD_FINISHED);
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vertical_blur.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vertical_blur.rs
new file mode 100644
index 0000000..60fd71b
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vertical_blur.rs
@@ -0,0 +1,59 @@
+#pragma version(1)
+#pragma rs_fp_relaxed
+
+#include "ip.rsh"
+
+static float saturation;
+static rs_matrix3x3 colorMat;
+
+void setSaturation(float sat) {
+ saturation = sat;
+
+ // Saturation
+ // Linear weights
+ //float rWeight = 0.3086f;
+ //float gWeight = 0.6094f;
+ //float bWeight = 0.0820f;
+
+ // Gamma 2.2 weights (we haven't converted our image to linear space yet for perf reasons)
+ float rWeight = 0.299f;
+ float gWeight = 0.587f;
+ float bWeight = 0.114f;
+
+ float oneMinusS = 1.0f - saturation;
+ rsMatrixSet(&colorMat, 0, 0, oneMinusS * rWeight + saturation);
+ rsMatrixSet(&colorMat, 0, 1, oneMinusS * rWeight);
+ rsMatrixSet(&colorMat, 0, 2, oneMinusS * rWeight);
+ rsMatrixSet(&colorMat, 1, 0, oneMinusS * gWeight);
+ rsMatrixSet(&colorMat, 1, 1, oneMinusS * gWeight + saturation);
+ rsMatrixSet(&colorMat, 1, 2, oneMinusS * gWeight);
+ rsMatrixSet(&colorMat, 2, 0, oneMinusS * bWeight);
+ rsMatrixSet(&colorMat, 2, 1, oneMinusS * bWeight);
+ rsMatrixSet(&colorMat, 2, 2, oneMinusS * bWeight + saturation);
+}
+
+void root(uchar4 *out, const void *usrData, uint32_t x, uint32_t y) {
+ const FilterStruct *fs = (const FilterStruct *)usrData;
+ float3 blurredPixel = 0;
+ const float *gPtr = fs->gaussian;
+ if ((y > fs->radius) && (y < (fs->height - fs->radius))) {
+ for (int r = -fs->radius; r <= fs->radius; r ++) {
+ const float4 *i = (const float4 *)rsGetElementAt(fs->ain, x, y + r);
+ blurredPixel += i->xyz * gPtr[0];
+ gPtr++;
+ }
+ } else {
+ for (int r = -fs->radius; r <= fs->radius; r ++) {
+ int validH = rsClamp((int)y + r, (int)0, (int)(fs->height - 1));
+ const float4 *i = (const float4 *)rsGetElementAt(fs->ain, x, validH);
+ blurredPixel += i->xyz * gPtr[0];
+ gPtr++;
+ }
+ }
+
+ float3 temp = rsMatrixMultiply(&colorMat, blurredPixel);
+ temp = clamp(temp, 0.f, 255.f);
+ out->xyz = convert_uchar3(temp);
+ out->w = 0xff;
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette.rsh b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette.rsh
new file mode 100644
index 0000000..a1e4ae5
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette.rsh
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+static float2 neg_center, axis_scale, inv_dimensions;
+static float sloped_neg_range, sloped_inv_max_dist, shade, opp_shade;
+
+void init_vignette(uint32_t dim_x, uint32_t dim_y, float center_x, float center_y,
+ float desired_scale, float desired_shade, float desired_slope) {
+
+ neg_center.x = -center_x;
+ neg_center.y = -center_y;
+ inv_dimensions.x = 1.f / (float)dim_x;
+ inv_dimensions.y = 1.f / (float)dim_y;
+
+ axis_scale = (float2)1.f;
+ if (dim_x > dim_y)
+ axis_scale.y = (float)dim_y / (float)dim_x;
+ else
+ axis_scale.x = (float)dim_x / (float)dim_y;
+
+ const float max_dist = 0.5 * length(axis_scale);
+ sloped_inv_max_dist = desired_slope * 1.f/max_dist;
+
+ // Range needs to be between 1.3 to 0.6. When scale is zero then range is
+ // 1.3 which means no vignette at all because the luminousity difference is
+ // less than 1/256. Expect input scale to be between 0.0 and 1.0.
+ const float neg_range = 0.7*sqrt(desired_scale) - 1.3;
+ sloped_neg_range = exp(neg_range * desired_slope);
+
+ shade = desired_shade;
+ opp_shade = 1.f - desired_shade;
+}
+
+void root(const uchar4 *in, uchar4 *out, uint32_t x, uint32_t y) {
+ // Convert x and y to floating point coordinates with center as origin
+ const float4 fin = convert_float4(*in);
+ const float2 inCoord = {(float)x, (float)y};
+ const float2 coord = mad(inCoord, inv_dimensions, neg_center);
+ const float sloped_dist_ratio = length(axis_scale * coord) * sloped_inv_max_dist;
+ const float lumen = opp_shade + shade / ( 1.0 + sloped_neg_range * exp(sloped_dist_ratio) );
+ float4 fout;
+ fout.rgb = fin.rgb * lumen;
+ fout.w = fin.w;
+ *out = convert_uchar4(fout);
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_full.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_full.rs
new file mode 100644
index 0000000..5fc2dda
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_full.rs
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.image2)
+
+#include "vignette.rsh"
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_relaxed.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_relaxed.rs
new file mode 100644
index 0000000..430b685
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_relaxed.rs
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.image2)
+#pragma rs_fp_relaxed
+
+#include "vignette.rsh"
+
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 292e4fc..943357a 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -61,6 +61,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
+import android.os.UserHandle;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
@@ -1193,12 +1194,12 @@
}
@Override
- public void sendBroadcastToUser(Intent intent, int userId) {
+ public void sendBroadcastAsUser(Intent intent, UserHandle user) {
// pass
}
@Override
- public void sendOrderedBroadcastToUser(Intent intent, int userId,
+ public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
BroadcastReceiver resultReceiver, Handler scheduler,
int initialCode, String initialData, Bundle initialExtras) {
// pass
diff --git a/wifi/java/android/net/wifi/RssiPacketCountInfo.java b/wifi/java/android/net/wifi/RssiPacketCountInfo.java
new file mode 100644
index 0000000..f549e1d
--- /dev/null
+++ b/wifi/java/android/net/wifi/RssiPacketCountInfo.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Bundle of RSSI and packet count information, for WiFi watchdog
+ *
+ * @see WifiWatchdogStateMachine
+ *
+ * @hide
+ */
+public class RssiPacketCountInfo implements Parcelable {
+
+ public int rssi;
+
+ public int txgood;
+
+ public int txbad;
+
+ public RssiPacketCountInfo() {
+ rssi = txgood = txbad = 0;
+ }
+
+ private RssiPacketCountInfo(Parcel in) {
+ rssi = in.readInt();
+ txgood = in.readInt();
+ txbad = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(rssi);
+ out.writeInt(txgood);
+ out.writeInt(txbad);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<RssiPacketCountInfo> CREATOR =
+ new Parcelable.Creator<RssiPacketCountInfo>() {
+ @Override
+ public RssiPacketCountInfo createFromParcel(Parcel in) {
+ return new RssiPacketCountInfo(in);
+ }
+
+ @Override
+ public RssiPacketCountInfo[] newArray(int size) {
+ return new RssiPacketCountInfo[size];
+ }
+ };
+}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 6e58a2d..3579b86 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -906,6 +906,17 @@
}
/**
+ * Return TX packet counter, for CTS test of WiFi watchdog.
+ * @param listener is the interface to receive result
+ *
+ * @hide for CTS test only
+ */
+ public void getTxPacketCount(TxPacketCountListener listener) {
+ validateChannel();
+ mAsyncChannel.sendMessage(RSSI_PKTCNT_FETCH, 0, putListener(listener));
+ }
+
+ /**
* Calculates the level of the signal. This should be used any time a signal
* is being shown.
*
@@ -1143,11 +1154,18 @@
/** @hide */
public static final int DISABLE_NETWORK_SUCCEEDED = BASE + 19;
+ /** @hide */
+ public static final int RSSI_PKTCNT_FETCH = BASE + 20;
+ /** @hide */
+ public static final int RSSI_PKTCNT_FETCH_SUCCEEDED = BASE + 21;
+ /** @hide */
+ public static final int RSSI_PKTCNT_FETCH_FAILED = BASE + 22;
+
/* For system use only */
/** @hide */
- public static final int ENABLE_TRAFFIC_STATS_POLL = BASE + 21;
+ public static final int ENABLE_TRAFFIC_STATS_POLL = BASE + 31;
/** @hide */
- public static final int TRAFFIC_STATS_POLL = BASE + 22;
+ public static final int TRAFFIC_STATS_POLL = BASE + 32;
/**
@@ -1212,6 +1230,21 @@
public void onFailure(int reason);
}
+ /** Interface for callback invocation on a TX packet count poll action {@hide} */
+ public interface TxPacketCountListener {
+ /**
+ * The operation succeeded
+ * @param count TX packet counter
+ */
+ public void onSuccess(int count);
+ /**
+ * The operation failed
+ * @param reason The reason for failure could be one of
+ * {@link #ERROR}, {@link #IN_PROGRESS} or {@link #BUSY}
+ */
+ public void onFailure(int reason);
+ }
+
private class ServiceHandler extends Handler {
ServiceHandler(Looper looper) {
super(looper);
@@ -1281,6 +1314,20 @@
((WpsListener) listener).onFailure(message.arg1);
}
break;
+ case WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED:
+ if (listener != null) {
+ RssiPacketCountInfo info = (RssiPacketCountInfo) message.obj;
+ if (info != null)
+ ((TxPacketCountListener) listener).onSuccess(info.txgood + info.txbad);
+ else
+ ((TxPacketCountListener) listener).onFailure(ERROR);
+ }
+ break;
+ case WifiManager.RSSI_PKTCNT_FETCH_FAILED:
+ if (listener != null) {
+ ((TxPacketCountListener) listener).onFailure(message.arg1);
+ }
+ break;
default:
//ignore
break;
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 28c1c5c..6abca65 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -52,7 +52,7 @@
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkUtils;
-import android.net.wifi.WifiWatchdogStateMachine.RssiPktcntStat;
+import android.net.wifi.RssiPacketCountInfo;
import android.net.wifi.WpsResult.Status;
import android.net.wifi.p2p.WifiP2pManager;
import android.net.wifi.p2p.WifiP2pService;
@@ -1189,7 +1189,7 @@
case CMD_RSSI_POLL:
case CMD_DELAYED_STOP_DRIVER:
case WifiMonitor.SCAN_RESULTS_EVENT:
- case WifiWatchdogStateMachine.RSSI_PKTCNT_FETCH:
+ case WifiManager.RSSI_PKTCNT_FETCH:
return false;
default:
return true;
@@ -1543,7 +1543,7 @@
/*
* Fetch TX packet counters on current connection
*/
- private void fetchPktcntNative(RssiPktcntStat stat) {
+ private void fetchPktcntNative(RssiPacketCountInfo info) {
String pktcntPoll = mWifiNative.pktcntPoll();
if (pktcntPoll != null) {
@@ -1553,9 +1553,9 @@
if (prop.length < 2) continue;
try {
if (prop[0].equals("TXGOOD")) {
- stat.txgood = Integer.parseInt(prop[1]);
+ info.txgood = Integer.parseInt(prop[1]);
} else if (prop[0].equals("TXBAD")) {
- stat.txbad = Integer.parseInt(prop[1]);
+ info.txbad = Integer.parseInt(prop[1]);
}
} catch (NumberFormatException e) {
//Ignore
@@ -1972,8 +1972,9 @@
replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
WifiManager.BUSY);
break;
- case WifiWatchdogStateMachine.RSSI_PKTCNT_FETCH:
- replyToMessage(message, WifiWatchdogStateMachine.RSSI_PKTCNT_FETCH_FAILED);
+ case WifiManager.RSSI_PKTCNT_FETCH:
+ replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_FAILED,
+ WifiManager.BUSY);
break;
default:
loge("Error! unhandled message" + message);
@@ -3176,13 +3177,12 @@
mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
}
break;
- case WifiWatchdogStateMachine.RSSI_PKTCNT_FETCH:
- RssiPktcntStat stat = (RssiPktcntStat) message.obj;
+ case WifiManager.RSSI_PKTCNT_FETCH:
+ RssiPacketCountInfo info = new RssiPacketCountInfo();
fetchRssiAndLinkSpeedNative();
- stat.rssi = mWifiInfo.getRssi();
- fetchPktcntNative(stat);
- replyToMessage(message, WifiWatchdogStateMachine.RSSI_PKTCNT_FETCH_SUCCEEDED,
- stat);
+ info.rssi = mWifiInfo.getRssi();
+ fetchPktcntNative(info);
+ replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED, info);
break;
default:
return NOT_HANDLED;
diff --git a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
index 7b4d113..29a53b6 100644
--- a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
@@ -30,6 +30,7 @@
import android.net.LinkProperties;
import android.net.NetworkInfo;
import android.net.Uri;
+import android.net.wifi.RssiPacketCountInfo;
import android.os.Message;
import android.os.SystemClock;
import android.provider.Settings;
@@ -105,9 +106,6 @@
/* Notifications from/to WifiStateMachine */
static final int POOR_LINK_DETECTED = BASE + 21;
static final int GOOD_LINK_DETECTED = BASE + 22;
- static final int RSSI_PKTCNT_FETCH = BASE + 23;
- static final int RSSI_PKTCNT_FETCH_SUCCEEDED = BASE + 24;
- static final int RSSI_PKTCNT_FETCH_FAILED = BASE + 25;
/*
* RSSI levels as used by notification icon
@@ -123,7 +121,7 @@
* <p>
* Larger threshold is more adaptive but increases sampling cost.
*/
- private static final int LINK_MONITOR_LEVEL_THRESHOLD = 4;
+ private static final int LINK_MONITOR_LEVEL_THRESHOLD = WifiManager.RSSI_LEVELS - 1;
/**
* Remember packet loss statistics of how many BSSIDs.
@@ -228,8 +226,8 @@
* Adaptive good link target to avoid flapping.
* When a poor link is detected, a good link target is calculated as follows:
* <p>
- * targetRSSI = min{ rssi | loss(rssi) < GOOD_LINK_LOSS_THRESHOLD } + rssi_adj[i],
- * where rssi is in the above GOOD_LINK_RSSI_RANGE.
+ * targetRSSI = min { rssi | loss(rssi) < GOOD_LINK_LOSS_THRESHOLD } + rssi_adj[i],
+ * where rssi is within the above GOOD_LINK_RSSI_RANGE.
* targetCount = sample_count[i] .
* <p>
* While WiFi is being avoided, we keep monitoring its signal strength.
@@ -241,7 +239,7 @@
* <p>
* Intuitively, larger index i makes it more difficult to get back to WiFi, avoiding flapping.
* In experiments, (+9 dB / 30 counts) makes it quite difficult to achieve.
- * Avoid using it unless flapping is really bad (say, last poor link is only 1min ago).
+ * Avoid using it unless flapping is really bad (say, last poor link is < 1 min ago).
*/
private static final GoodLinkTarget[] GOOD_LINK_TARGET = {
/* rssi_adj, sample_count, reduce_time */
@@ -591,8 +589,8 @@
case EVENT_BSSID_CHANGE:
case CMD_DELAYED_WALLED_GARDEN_CHECK:
case CMD_RSSI_FETCH:
- case RSSI_PKTCNT_FETCH_SUCCEEDED:
- case RSSI_PKTCNT_FETCH_FAILED:
+ case WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED:
+ case WifiManager.RSSI_PKTCNT_FETCH_FAILED:
// ignore
break;
case EVENT_SCREEN_ON:
@@ -764,15 +762,15 @@
case CMD_RSSI_FETCH:
if (msg.arg1 == mRssiFetchToken) {
- mWsmChannel.sendMessage(RSSI_PKTCNT_FETCH, new RssiPktcntStat());
+ mWsmChannel.sendMessage(WifiManager.RSSI_PKTCNT_FETCH);
sendMessageDelayed(obtainMessage(CMD_RSSI_FETCH, ++mRssiFetchToken, 0),
LINK_SAMPLING_INTERVAL_MS);
}
break;
- case RSSI_PKTCNT_FETCH_SUCCEEDED:
- RssiPktcntStat stat = (RssiPktcntStat) msg.obj;
- int rssi = stat.rssi;
+ case WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED:
+ RssiPacketCountInfo info = (RssiPacketCountInfo) msg.obj;
+ int rssi = info.rssi;
if (DBG) logd("Fetch RSSI succeed, rssi=" + rssi);
long time = mCurrentBssid.mBssidAvoidTimeMax - SystemClock.elapsedRealtime();
@@ -795,7 +793,7 @@
}
break;
- case RSSI_PKTCNT_FETCH_FAILED:
+ case WifiManager.RSSI_PKTCNT_FETCH_FAILED:
if (DBG) logd("RSSI_FETCH_FAILED");
break;
@@ -944,18 +942,18 @@
if (!mIsScreenOn) {
transitionTo(mOnlineState);
} else if (msg.arg1 == mRssiFetchToken) {
- mWsmChannel.sendMessage(RSSI_PKTCNT_FETCH, new RssiPktcntStat());
+ mWsmChannel.sendMessage(WifiManager.RSSI_PKTCNT_FETCH);
sendMessageDelayed(obtainMessage(CMD_RSSI_FETCH, ++mRssiFetchToken, 0),
LINK_SAMPLING_INTERVAL_MS);
}
break;
- case RSSI_PKTCNT_FETCH_SUCCEEDED:
- RssiPktcntStat stat = (RssiPktcntStat) msg.obj;
- int rssi = stat.rssi;
+ case WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED:
+ RssiPacketCountInfo info = (RssiPacketCountInfo) msg.obj;
+ int rssi = info.rssi;
int mrssi = (mLastRssi + rssi) / 2;
- int txbad = stat.txbad;
- int txgood = stat.txgood;
+ int txbad = info.txbad;
+ int txgood = info.txgood;
if (DBG) logd("Fetch RSSI succeed, rssi=" + rssi + " mrssi=" + mrssi + " txbad="
+ txbad + " txgood=" + txgood);
@@ -1003,7 +1001,7 @@
mLastRssi = rssi;
break;
- case RSSI_PKTCNT_FETCH_FAILED:
+ case WifiManager.RSSI_PKTCNT_FETCH_FAILED:
// can happen if we are waiting to get a disconnect notification
if (DBG) logd("RSSI_FETCH_FAILED");
break;
@@ -1159,15 +1157,6 @@
}
/**
- * Bundle of RSSI and packet count information
- */
- public class RssiPktcntStat {
- public int rssi;
- public int txgood;
- public int txbad;
- }
-
- /**
* Bundle of good link count parameters
*/
private static class GoodLinkTarget {