diff options
Diffstat (limited to 'core/java/android')
30 files changed, 1558 insertions, 265 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 1f6f421..63ac42e 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -1238,6 +1238,97 @@ public class ActivityManager { } } + /** + * Information you can retrieve about the WindowManager StackBox hierarchy. + * @hide + */ + public static class StackBoxInfo implements Parcelable { + public int stackBoxId; + public float weight; + public boolean vertical; + public Rect bounds; + public StackBoxInfo[] children; + public int stackId; + public StackInfo stack; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(stackBoxId); + dest.writeFloat(weight); + dest.writeInt(vertical ? 1 : 0); + bounds.writeToParcel(dest, flags); + dest.writeInt(stackId); + if (children != null) { + children[0].writeToParcel(dest, flags); + children[1].writeToParcel(dest, flags); + } else { + stack.writeToParcel(dest, flags); + } + } + + public void readFromParcel(Parcel source) { + stackBoxId = source.readInt(); + weight = source.readFloat(); + vertical = source.readInt() == 1; + bounds = Rect.CREATOR.createFromParcel(source); + stackId = source.readInt(); + if (stackId == -1) { + children = new StackBoxInfo[2]; + children[0] = StackBoxInfo.CREATOR.createFromParcel(source); + children[1] = StackBoxInfo.CREATOR.createFromParcel(source); + } else { + stack = StackInfo.CREATOR.createFromParcel(source); + } + } + + public static final Creator<StackBoxInfo> CREATOR = + new Creator<ActivityManager.StackBoxInfo>() { + + @Override + public StackBoxInfo createFromParcel(Parcel source) { + return new StackBoxInfo(source); + } + + @Override + public StackBoxInfo[] newArray(int size) { + return new StackBoxInfo[size]; + } + }; + + public StackBoxInfo() { + } + + public StackBoxInfo(Parcel source) { + readFromParcel(source); + } + + public String toString(String prefix) { + StringBuilder sb = new StringBuilder(256); + sb.append(prefix); sb.append("Box id=" + stackBoxId); sb.append(" weight=" + weight); + sb.append(" vertical=" + vertical); sb.append(" bounds=" + bounds.toShortString()); + sb.append("\n"); + if (children != null) { + sb.append(prefix); sb.append("First child=\n"); + sb.append(children[0].toString(prefix + " ")); + sb.append(prefix); sb.append("Second child=\n"); + sb.append(children[1].toString(prefix + " ")); + } else { + sb.append(prefix); sb.append("Stack=\n"); + sb.append(stack.toString(prefix + " ")); + } + return sb.toString(); + } + + @Override + public String toString() { + return toString(""); + } + } /** * Information you can retrieve about an ActivityStack in the system. @@ -1249,9 +1340,6 @@ public class ActivityManager { public int[] taskIds; public String[] taskNames; - public StackInfo() { - } - @Override public int describeContents() { return 0; @@ -1287,22 +1375,29 @@ public class ActivityManager { } }; + public StackInfo() { + } + private StackInfo(Parcel source) { readFromParcel(source); } - @Override - public String toString() { + public String toString(String prefix) { StringBuilder sb = new StringBuilder(256); - sb.append("Stack id="); sb.append(stackId); + sb.append(prefix); sb.append("Stack id="); sb.append(stackId); sb.append(" bounds="); sb.append(bounds.toShortString()); sb.append("\n"); - final String prefix = " "; + prefix = prefix + " "; for (int i = 0; i < taskIds.length; ++i) { sb.append(prefix); sb.append("taskId="); sb.append(taskIds[i]); sb.append(": "); sb.append(taskNames[i]); sb.append("\n"); } return sb.toString(); } + + @Override + public String toString() { + return toString(""); + } } /** @@ -1378,10 +1473,12 @@ public class ActivityManager { public ProcessErrorStateInfo() { } + @Override public int describeContents() { return 0; } + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(condition); dest.writeString(processName); @@ -1392,7 +1489,7 @@ public class ActivityManager { dest.writeString(longMsg); dest.writeString(stackTrace); } - + public void readFromParcel(Parcel source) { condition = source.readInt(); processName = source.readString(); diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index b197f90..27e20b9 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -16,6 +16,7 @@ package android.app; +import android.app.ActivityManager.StackBoxInfo; import android.content.ComponentName; import android.content.IIntentReceiver; import android.content.IIntentSender; @@ -632,16 +633,16 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM case RESIZE_STACK_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); - int stackId = data.readInt(); + int stackBoxId = data.readInt(); float weight = data.readFloat(); - resizeStack(stackId, weight); + resizeStackBox(stackBoxId, weight); reply.writeNoException(); return true; } - case GET_STACKS_TRANSACTION: { + case GET_STACK_BOXES_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); - List<ActivityManager.StackInfo> list = getStacks(); + List<StackBoxInfo> list = getStackBoxes(); reply.writeNoException(); reply.writeTypedList(list); return true; @@ -1409,7 +1410,8 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM data.enforceInterface(IActivityManager.descriptor); String pkg = data.readString(); int appid = data.readInt(); - killApplicationWithAppId(pkg, appid); + String reason = data.readString(); + killApplicationWithAppId(pkg, appid, reason); reply.writeNoException(); return true; } @@ -2612,14 +2614,14 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } @Override - public int createStack(int taskId, int relativeStackId, int position, float weight) + public int createStack(int taskId, int relativeStackBoxId, int position, float weight) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeInt(taskId); - data.writeInt(relativeStackId); + data.writeInt(relativeStackBoxId); data.writeInt(position); data.writeFloat(weight); mRemote.transact(CREATE_STACK_TRANSACTION, data, reply, 0); @@ -2644,12 +2646,12 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } @Override - public void resizeStack(int stackId, float weight) throws RemoteException + public void resizeStackBox(int stackBoxId, float weight) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); - data.writeInt(stackId); + data.writeInt(stackBoxId); data.writeFloat(weight); mRemote.transact(RESIZE_STACK_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY); reply.readException(); @@ -2657,15 +2659,14 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } @Override - public List<ActivityManager.StackInfo> getStacks() throws RemoteException + public List<StackBoxInfo> getStackBoxes() throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); - mRemote.transact(GET_STACKS_TRANSACTION, data, reply, 0); + mRemote.transact(GET_STACK_BOXES_TRANSACTION, data, reply, 0); reply.readException(); - ArrayList<ActivityManager.StackInfo> list - = reply.createTypedArrayList(ActivityManager.StackInfo.CREATOR); + ArrayList<StackBoxInfo> list = reply.createTypedArrayList(StackBoxInfo.CREATOR); data.recycle(); reply.recycle(); return list; @@ -3692,12 +3693,14 @@ class ActivityManagerProxy implements IActivityManager data.recycle(); } - public void killApplicationWithAppId(String pkg, int appid) throws RemoteException { + public void killApplicationWithAppId(String pkg, int appid, String reason) + throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeString(pkg); data.writeInt(appid); + data.writeString(reason); mRemote.transact(KILL_APPLICATION_WITH_APPID_TRANSACTION, data, reply, 0); reply.readException(); data.recycle(); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index d1bf0af..4a41896 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -895,17 +895,18 @@ public final class ActivityThread { @Override public Debug.MemoryInfo dumpMemInfo(FileDescriptor fd, boolean checkin, - boolean all, String[] args) { + boolean dumpInfo, boolean dumpDalvik, String[] args) { FileOutputStream fout = new FileOutputStream(fd); PrintWriter pw = new PrintWriter(fout); try { - return dumpMemInfo(pw, checkin, all); + return dumpMemInfo(pw, checkin, dumpInfo, dumpDalvik); } finally { pw.flush(); } } - private Debug.MemoryInfo dumpMemInfo(PrintWriter pw, boolean checkin, boolean all) { + private Debug.MemoryInfo dumpMemInfo(PrintWriter pw, boolean checkin, boolean dumpInfo, + boolean dumpDalvik) { long nativeMax = Debug.getNativeHeapSize() / 1024; long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024; long nativeFree = Debug.getNativeHeapFreeSize() / 1024; @@ -913,7 +914,7 @@ public final class ActivityThread { Debug.MemoryInfo memInfo = new Debug.MemoryInfo(); Debug.getMemoryInfo(memInfo); - if (!all) { + if (!dumpInfo) { return memInfo; } @@ -1040,16 +1041,20 @@ public final class ActivityThread { } // otherwise, show human-readable format - printRow(pw, HEAP_COLUMN, "", "", "Swapable","Shared", "Private", "Shared", "Private", "Heap", "Heap", "Heap"); - printRow(pw, HEAP_COLUMN, "", "Pss", "Pss", "Dirty", "Dirty", "Clean", "Clean", "Size", "Alloc", "Free"); - printRow(pw, HEAP_COLUMN, "", "------", "------", "------", "------", "------", "------", "------", "------", - "------"); - printRow(pw, HEAP_COLUMN, "Native", memInfo.nativePss, memInfo.nativeSwappablePss, memInfo.nativeSharedDirty, - memInfo.nativePrivateDirty, memInfo.nativeSharedClean, memInfo.nativePrivateClean,nativeMax, - nativeAllocated, nativeFree); - printRow(pw, HEAP_COLUMN, "Dalvik", memInfo.dalvikPss, memInfo.dalvikSwappablePss, memInfo.dalvikSharedDirty, - memInfo.dalvikPrivateDirty, memInfo.dalvikSharedClean, memInfo.dalvikPrivateClean, dalvikMax, dalvikAllocated, - dalvikFree); + printRow(pw, HEAP_COLUMN, "", "Pss", "Pss","Shared", "Private", "Shared", "Private", + "Heap", "Heap", "Heap"); + printRow(pw, HEAP_COLUMN, "", "Total", "Clean", "Dirty", "Dirty", "Clean", "Clean", + "Size", "Alloc", "Free"); + printRow(pw, HEAP_COLUMN, "", "------", "------", "------", "------", "------", + "------", "------", "------", "------"); + printRow(pw, HEAP_COLUMN, "Native Heap", memInfo.nativePss, memInfo.nativeSwappablePss, + memInfo.nativeSharedDirty, + memInfo.nativePrivateDirty, memInfo.nativeSharedClean, + memInfo.nativePrivateClean, nativeMax, nativeAllocated, nativeFree); + printRow(pw, HEAP_COLUMN, "Dalvik Heap", memInfo.dalvikPss, memInfo.dalvikSwappablePss, + memInfo.dalvikSharedDirty, + memInfo.dalvikPrivateDirty, memInfo.dalvikSharedClean, + memInfo.dalvikPrivateClean, dalvikMax, dalvikAllocated, dalvikFree); int otherPss = memInfo.otherPss; int otherSwappablePss = memInfo.otherSwappablePss; @@ -1059,42 +1064,56 @@ public final class ActivityThread { int otherPrivateClean = memInfo.otherPrivateClean; for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_STATS; i++) { - printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i), - memInfo.getOtherPss(i), memInfo.getOtherSwappablePss(i), memInfo.getOtherSharedDirty(i), - memInfo.getOtherPrivateDirty(i), memInfo.getOtherSharedClean(i), memInfo.getOtherPrivateClean(i), - "", "", ""); - otherPss -= memInfo.getOtherPss(i); - otherSwappablePss -= memInfo.getOtherSwappablePss(i); - otherSharedDirty -= memInfo.getOtherSharedDirty(i); - otherPrivateDirty -= memInfo.getOtherPrivateDirty(i); - otherSharedClean -= memInfo.getOtherSharedClean(i); - otherPrivateClean -= memInfo.getOtherPrivateClean(i); + final int myPss = memInfo.getOtherPss(i); + final int mySwappablePss = memInfo.getOtherSwappablePss(i); + final int mySharedDirty = memInfo.getOtherSharedDirty(i); + final int myPrivateDirty = memInfo.getOtherPrivateDirty(i); + final int mySharedClean = memInfo.getOtherSharedClean(i); + final int myPrivateClean = memInfo.getOtherPrivateClean(i); + if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0 + || mySharedClean != 0 || myPrivateClean != 0) { + printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i), + myPss, mySwappablePss, mySharedDirty, myPrivateDirty, + mySharedClean, myPrivateClean, "", "", ""); + otherPss -= myPss; + otherSwappablePss -= mySwappablePss; + otherSharedDirty -= mySharedDirty; + otherPrivateDirty -= myPrivateDirty; + otherSharedClean -= mySharedClean; + otherPrivateClean -= myPrivateClean; + } } printRow(pw, HEAP_COLUMN, "Unknown", otherPss, otherSwappablePss, otherSharedDirty, otherPrivateDirty, otherSharedClean, otherPrivateClean,"", "", ""); - printRow(pw, HEAP_COLUMN, "TOTAL", memInfo.getTotalPss(), memInfo.getTotalSwappablePss(), - memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(), - memInfo.getTotalSharedClean(), memInfo.getTotalPrivateClean(), nativeMax+dalvikMax, - nativeAllocated+dalvikAllocated, nativeFree+dalvikFree); - - pw.println(" "); - pw.println(" Dalvik"); - - for (int i=Debug.MemoryInfo.NUM_OTHER_STATS; - i<Debug.MemoryInfo.NUM_OTHER_STATS + Debug.MemoryInfo.NUM_DVK_STATS; i++) { - printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i), - memInfo.getOtherPss(i), memInfo.getOtherSwappablePss(i), memInfo.getOtherSharedDirty(i), - memInfo.getOtherPrivateDirty(i), memInfo.getOtherSharedClean(i), - memInfo.getOtherPrivateClean(i), "", "", ""); - otherPss -= memInfo.getOtherPss(i); - otherSwappablePss -= memInfo.getOtherSwappablePss(i); - otherSharedDirty -= memInfo.getOtherSharedDirty(i); - otherPrivateDirty -= memInfo.getOtherPrivateDirty(i); - otherSharedClean -= memInfo.getOtherSharedClean(i); - otherPrivateClean -= memInfo.getOtherPrivateClean(i); + printRow(pw, HEAP_COLUMN, "TOTAL", memInfo.getTotalPss(), + memInfo.getTotalSwappablePss(), + memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(), + memInfo.getTotalSharedClean(), memInfo.getTotalPrivateClean(), + nativeMax+dalvikMax, + nativeAllocated+dalvikAllocated, nativeFree+dalvikFree); + + if (dumpDalvik) { + pw.println(" "); + pw.println(" Dalvik Details"); + + for (int i=Debug.MemoryInfo.NUM_OTHER_STATS; + i<Debug.MemoryInfo.NUM_OTHER_STATS + Debug.MemoryInfo.NUM_DVK_STATS; i++) { + final int myPss = memInfo.getOtherPss(i); + final int mySwappablePss = memInfo.getOtherSwappablePss(i); + final int mySharedDirty = memInfo.getOtherSharedDirty(i); + final int myPrivateDirty = memInfo.getOtherPrivateDirty(i); + final int mySharedClean = memInfo.getOtherSharedClean(i); + final int myPrivateClean = memInfo.getOtherPrivateClean(i); + if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0 + || mySharedClean != 0 || myPrivateClean != 0) { + printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i), + myPss, mySwappablePss, mySharedDirty, myPrivateDirty, + mySharedClean, myPrivateClean, "", "", ""); + } + } } pw.println(" "); diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index b1c58f2..e903447 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -524,12 +524,13 @@ public abstract class ApplicationThreadNative extends Binder data.enforceInterface(IApplicationThread.descriptor); ParcelFileDescriptor fd = data.readFileDescriptor(); boolean checkin = data.readInt() != 0; - boolean all = data.readInt() != 0; + boolean dumpInfo = data.readInt() != 0; + boolean dumpDalvik = data.readInt() != 0; String[] args = data.readStringArray(); Debug.MemoryInfo mi = null; if (fd != null) { try { - mi = dumpMemInfo(fd.getFileDescriptor(), checkin, all, args); + mi = dumpMemInfo(fd.getFileDescriptor(), checkin, dumpInfo, dumpDalvik, args); } finally { try { fd.close(); @@ -1159,14 +1160,15 @@ class ApplicationThreadProxy implements IApplicationThread { IBinder.FLAG_ONEWAY); } - public Debug.MemoryInfo dumpMemInfo(FileDescriptor fd, boolean checkin, boolean all, - String[] args) throws RemoteException { + public Debug.MemoryInfo dumpMemInfo(FileDescriptor fd, boolean checkin, boolean dumpInfo, + boolean dumpDalvik, String[] args) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); data.writeFileDescriptor(fd); data.writeInt(checkin ? 1 : 0); - data.writeInt(all ? 1 : 0); + data.writeInt(dumpInfo ? 1 : 0); + data.writeInt(dumpDalvik ? 1 : 0); data.writeStringArray(args); mRemote.transact(DUMP_MEM_INFO_TRANSACTION, data, reply, 0); reply.readException(); diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 334a304..b48eed2 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -18,7 +18,7 @@ package android.app; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManager.RunningServiceInfo; -import android.app.ActivityManager.StackInfo; +import android.app.ActivityManager.StackBoxInfo; import android.content.ComponentName; import android.content.ContentProviderNative; import android.content.IContentProvider; @@ -115,11 +115,11 @@ public interface IActivityManager extends IInterface { public void moveTaskToBack(int task) throws RemoteException; public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) throws RemoteException; public void moveTaskBackwards(int task) throws RemoteException; - public int createStack(int taskId, int relativeStackId, int position, float weight) + public int createStack(int taskId, int relativeStackBoxId, int position, float weight) throws RemoteException; public void moveTaskToStack(int taskId, int stackId, boolean toTop) throws RemoteException; - public void resizeStack(int stackId, float weight) throws RemoteException; - public List<StackInfo> getStacks() throws RemoteException; + public void resizeStackBox(int stackBoxId, float weight) throws RemoteException; + public List<StackBoxInfo> getStackBoxes() throws RemoteException; public void setFocusedStack(int stackId) throws RemoteException; public int getTaskForActivity(IBinder token, boolean onlyRoot) throws RemoteException; /* oneway */ @@ -283,7 +283,8 @@ public interface IActivityManager extends IInterface { public void stopAppSwitches() throws RemoteException; public void resumeAppSwitches() throws RemoteException; - public void killApplicationWithAppId(String pkg, int appid) throws RemoteException; + public void killApplicationWithAppId(String pkg, int appid, String reason) + throws RemoteException; public void closeSystemDialogs(String reason) throws RemoteException; @@ -661,6 +662,6 @@ public interface IActivityManager extends IInterface { int CREATE_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+167; int MOVE_TASK_TO_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+168; int RESIZE_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+169; - int GET_STACKS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+170; + int GET_STACK_BOXES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+170; int SET_FOCUSED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+171; } diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index 3189b31..a009bd3 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -126,8 +126,8 @@ public interface IApplicationThread extends IInterface { void setCoreSettings(Bundle coreSettings) throws RemoteException; void updatePackageCompatibilityInfo(String pkg, CompatibilityInfo info) throws RemoteException; void scheduleTrimMemory(int level) throws RemoteException; - Debug.MemoryInfo dumpMemInfo(FileDescriptor fd, boolean checkin, boolean all, - String[] args) throws RemoteException; + Debug.MemoryInfo dumpMemInfo(FileDescriptor fd, boolean checkin, boolean dumpInfo, + boolean dumpDalvik, String[] args) throws RemoteException; void dumpGfxInfo(FileDescriptor fd, String[] args) throws RemoteException; void dumpDbInfo(FileDescriptor fd, String[] args) throws RemoteException; void unstableProviderDied(IBinder provider) throws RemoteException; diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java index 267555a..b13b24a 100644 --- a/core/java/android/app/LoaderManager.java +++ b/core/java/android/app/LoaderManager.java @@ -204,13 +204,13 @@ class LoaderManagerImpl extends LoaderManager { // These are the currently active loaders. A loader is here // from the time its load is started until it has been explicitly // stopped or restarted by the application. - final SparseArray<LoaderInfo> mLoaders = new SparseArray<LoaderInfo>(); + final SparseArray<LoaderInfo> mLoaders = new SparseArray<LoaderInfo>(0); // These are previously run loaders. This list is maintained internally // to avoid destroying a loader while an application is still using it. // It allows an application to restart a loader, but continue using its // previously run loader until the new loader's data is available. - final SparseArray<LoaderInfo> mInactiveLoaders = new SparseArray<LoaderInfo>(); + final SparseArray<LoaderInfo> mInactiveLoaders = new SparseArray<LoaderInfo>(0); final String mWho; diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index 25c790f..bdd0adb 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -207,8 +207,7 @@ public final class PendingIntent implements Parcelable { * * @param context The Context in which this PendingIntent should start * the activity. - * @param requestCode Private request code for the sender (currently - * not used). + * @param requestCode Private request code for the sender * @param intent Intent of the activity to be launched. * @param flags May be {@link #FLAG_ONE_SHOT}, {@link #FLAG_NO_CREATE}, * {@link #FLAG_CANCEL_CURRENT}, {@link #FLAG_UPDATE_CURRENT}, @@ -239,8 +238,7 @@ public final class PendingIntent implements Parcelable { * * @param context The Context in which this PendingIntent should start * the activity. - * @param requestCode Private request code for the sender (currently - * not used). + * @param requestCode Private request code for the sender * @param intent Intent of the activity to be launched. * @param flags May be {@link #FLAG_ONE_SHOT}, {@link #FLAG_NO_CREATE}, * {@link #FLAG_CANCEL_CURRENT}, {@link #FLAG_UPDATE_CURRENT}, @@ -332,8 +330,7 @@ public final class PendingIntent implements Parcelable { * * @param context The Context in which this PendingIntent should start * the activity. - * @param requestCode Private request code for the sender (currently - * not used). + * @param requestCode Private request code for the sender * @param intents Array of Intents of the activities to be launched. * @param flags May be {@link #FLAG_ONE_SHOT}, {@link #FLAG_NO_CREATE}, * {@link #FLAG_CANCEL_CURRENT}, {@link #FLAG_UPDATE_CURRENT}, @@ -383,8 +380,7 @@ public final class PendingIntent implements Parcelable { * * @param context The Context in which this PendingIntent should start * the activity. - * @param requestCode Private request code for the sender (currently - * not used). + * @param requestCode Private request code for the sender * @param intents Array of Intents of the activities to be launched. * @param flags May be {@link #FLAG_ONE_SHOT}, {@link #FLAG_NO_CREATE}, * {@link #FLAG_CANCEL_CURRENT}, {@link #FLAG_UPDATE_CURRENT}, @@ -454,8 +450,7 @@ public final class PendingIntent implements Parcelable { * * @param context The Context in which this PendingIntent should perform * the broadcast. - * @param requestCode Private request code for the sender (currently - * not used). + * @param requestCode Private request code for the sender * @param intent The Intent to be broadcast. * @param flags May be {@link #FLAG_ONE_SHOT}, {@link #FLAG_NO_CREATE}, * {@link #FLAG_CANCEL_CURRENT}, {@link #FLAG_UPDATE_CURRENT}, @@ -509,8 +504,7 @@ public final class PendingIntent implements Parcelable { * * @param context The Context in which this PendingIntent should start * the service. - * @param requestCode Private request code for the sender (currently - * not used). + * @param requestCode Private request code for the sender * @param intent An Intent describing the service to be started. * @param flags May be {@link #FLAG_ONE_SHOT}, {@link #FLAG_NO_CREATE}, * {@link #FLAG_CANCEL_CURRENT}, {@link #FLAG_UPDATE_CURRENT}, diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java index 4df6e7c..77e5f84 100644 --- a/core/java/android/bluetooth/BluetoothGatt.java +++ b/core/java/android/bluetooth/BluetoothGatt.java @@ -70,9 +70,6 @@ public final class BluetoothGatt implements BluetoothProfile { private List<BluetoothGattService> mServices; - /** A GATT operation failed */ - public static final int GATT_FAILURE = 0; - /** A GATT operation completed successfully */ public static final int GATT_SUCCESS = 0; @@ -97,6 +94,9 @@ public final class BluetoothGatt implements BluetoothProfile { /** A write operation exceeds the maximum length of the attribute */ public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd; + /** A GATT operation failed, errors other than the above */ + public static final int GATT_FAILURE = 0x101; + /** * No authentication required. * @hide diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java index 288d55f..875e8de 100644 --- a/core/java/android/content/pm/RegisteredServicesCache.java +++ b/core/java/android/content/pm/RegisteredServicesCache.java @@ -82,7 +82,7 @@ public abstract class RegisteredServicesCache<V> { @GuardedBy("mServicesLock") private boolean mPersistentServicesFileDidNotExist; @GuardedBy("mServicesLock") - private final SparseArray<UserServices<V>> mUserServices = new SparseArray<UserServices<V>>(); + private final SparseArray<UserServices<V>> mUserServices = new SparseArray<UserServices<V>>(2); private static class UserServices<V> { @GuardedBy("mServicesLock") diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index 905ae0d..68db33a 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -853,11 +853,13 @@ public final class Configuration implements Parcelable, Comparable<Configuration changed |= ActivityInfo.CONFIG_SCREEN_SIZE; screenHeightDp = delta.screenHeightDp; } - if (delta.smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) { - changed |= ActivityInfo.CONFIG_SCREEN_SIZE; + if (delta.smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED + && smallestScreenWidthDp != delta.smallestScreenWidthDp) { + changed |= ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE; smallestScreenWidthDp = delta.smallestScreenWidthDp; } - if (delta.densityDpi != DENSITY_DPI_UNDEFINED) { + if (delta.densityDpi != DENSITY_DPI_UNDEFINED && + densityDpi != delta.densityDpi) { changed |= ActivityInfo.CONFIG_DENSITY; densityDpi = delta.densityDpi; } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index c7976c3..cff974d 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -99,11 +99,11 @@ public class Resources { /*package*/ final Configuration mTmpConfig = new Configuration(); /*package*/ TypedValue mTmpValue = new TypedValue(); /*package*/ final LongSparseArray<WeakReference<Drawable.ConstantState> > mDrawableCache - = new LongSparseArray<WeakReference<Drawable.ConstantState> >(); + = new LongSparseArray<WeakReference<Drawable.ConstantState> >(0); /*package*/ final LongSparseArray<WeakReference<ColorStateList> > mColorStateListCache - = new LongSparseArray<WeakReference<ColorStateList> >(); + = new LongSparseArray<WeakReference<ColorStateList> >(0); /*package*/ final LongSparseArray<WeakReference<Drawable.ConstantState> > mColorDrawableCache - = new LongSparseArray<WeakReference<Drawable.ConstantState> >(); + = new LongSparseArray<WeakReference<Drawable.ConstantState> >(0); /*package*/ boolean mPreloading; /*package*/ TypedArray mCachedStyledAttributes = null; diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java index 1fc1226..e2d9724 100644 --- a/core/java/android/database/DatabaseUtils.java +++ b/core/java/android/database/DatabaseUtils.java @@ -792,6 +792,18 @@ public class DatabaseUtils { } /** + * Query the table to check whether a table is empty or not + * @param db the database the table is in + * @param table the name of the table to query + * @return True if the table is empty + * @hide + */ + public static boolean queryIsEmpty(SQLiteDatabase db, String table) { + long isEmpty = longForQuery(db, "select exists(select 1 from " + table + ")", null); + return isEmpty == 0; + } + + /** * Utility method to run the query on the db and return the value in the * first column of the first row. */ diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index d8e30e2..362ae29 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -108,19 +108,19 @@ public final class Debug * process. The returns info broken down by dalvik, native, and other. All results are in kB. */ public static class MemoryInfo implements Parcelable { - /** The proportional set size for dalvik. */ + /** The proportional set size for dalvik heap. (Doesn't include other Dalvik overhead.) */ public int dalvikPss; - /** The proportional set size that is swappable for dalvik. */ + /** The proportional set size that is swappable for dalvik heap. */ /** @hide We may want to expose this, eventually. */ public int dalvikSwappablePss; - /** The private dirty pages used by dalvik. */ + /** The private dirty pages used by dalvik heap. */ public int dalvikPrivateDirty; - /** The shared dirty pages used by dalvik. */ + /** The shared dirty pages used by dalvik heap. */ public int dalvikSharedDirty; - /** The private clean pages used by dalvik. */ + /** The private clean pages used by dalvik heap. */ /** @hide We may want to expose this, eventually. */ public int dalvikPrivateClean; - /** The shared clean pages used by dalvik. */ + /** The shared clean pages used by dalvik heap. */ /** @hide We may want to expose this, eventually. */ public int dalvikSharedClean; @@ -157,7 +157,7 @@ public final class Debug public int otherSharedClean; /** @hide */ - public static final int NUM_OTHER_STATS = 12; + public static final int NUM_OTHER_STATS = 13; /** @hide */ public static final int NUM_DVK_STATS = 5; @@ -263,23 +263,24 @@ public final class Debug /* @hide */ public static String getOtherLabel(int which) { switch (which) { - case 0: return "Stack"; - case 1: return "Cursor"; - case 2: return "Ashmem"; - case 3: return "Other dev"; - case 4: return ".so mmap"; - case 5: return ".jar mmap"; - case 6: return ".apk mmap"; - case 7: return ".ttf mmap"; - case 8: return ".dex mmap"; - case 9: return "code mmap"; - case 10: return "image mmap"; - case 11: return "Other mmap"; - case 12: return ".Heap"; - case 13: return ".LOS"; - case 14: return ".LinearAlloc"; - case 15: return ".GC"; - case 16: return ".JITCache"; + case 0: return "Dalvik Other"; + case 1: return "Stack"; + case 2: return "Cursor"; + case 3: return "Ashmem"; + case 4: return "Other dev"; + case 5: return ".so mmap"; + case 6: return ".jar mmap"; + case 7: return ".apk mmap"; + case 8: return ".ttf mmap"; + case 9: return ".dex mmap"; + case 10: return "code mmap"; + case 11: return "image mmap"; + case 12: return "Other mmap"; + case 13: return ".Heap"; + case 14: return ".LOS"; + case 15: return ".LinearAlloc"; + case 16: return ".GC"; + case 17: return ".JITCache"; default: return "????"; } } diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java index e53cb5e..bb3d296 100644 --- a/core/java/android/os/Trace.java +++ b/core/java/android/os/Trace.java @@ -67,6 +67,8 @@ public final class Trace { public static final long TRACE_TAG_RESOURCES = 1L << 13; /** @hide */ public static final long TRACE_TAG_DALVIK = 1L << 14; + /** @hide */ + public static final long TRACE_TAG_RS = 1L << 15; private static final long TRACE_TAG_NOT_READY = 1L << 63; private static final int MAX_SECTION_NAME_LEN = 127; diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java index e343e83..6c02965 100644 --- a/core/java/android/preference/Preference.java +++ b/core/java/android/preference/Preference.java @@ -1072,6 +1072,9 @@ public class Preference implements Comparable<Preference>, OnDependencyChangeLis || (mOrder == DEFAULT_ORDER && another.mOrder != DEFAULT_ORDER)) { // Do order comparison return mOrder - another.mOrder; + } else if (mTitle == another.mTitle) { + // If titles are null or share same object comparison + return 0; } else if (mTitle == null) { return 1; } else if (another.mTitle == null) { diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java index e8cc24b..19f8678 100644 --- a/core/java/android/service/notification/StatusBarNotification.java +++ b/core/java/android/service/notification/StatusBarNotification.java @@ -190,13 +190,13 @@ public class StatusBarNotification implements Parcelable { return pkg; } - /** The id supplied to {@link android.app.NotificationManager#notify}. */ + /** The id supplied to {@link android.app.NotificationManager#notify(int,Notification)}. */ public int getId() { return id; } - /** The tag supplied to {@link android.app.NotificationManager#notify}, or null if no tag - * was specified. */ + /** The tag supplied to {@link android.app.NotificationManager#notify(int,Notification)}, + * or null if no tag was specified. */ public String getTag() { return tag; } @@ -217,7 +217,7 @@ public class StatusBarNotification implements Parcelable { } /** The {@link android.app.Notification} supplied to - * {@link android.app.NotificationManager#notify}. */ + * {@link android.app.NotificationManager#notify(int,Notification)}. */ public Notification getNotification() { return notification; } diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java new file mode 100644 index 0000000..70f7d2e --- /dev/null +++ b/core/java/android/util/ArrayMap.java @@ -0,0 +1,615 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +/** + * ArrayMap is a generic key->value mapping data structure that is + * designed to be more memory efficient than a traditional {@link java.util.HashMap}. + * It keeps its mappings in an array data structure -- an integer array of hash + * codes for each item, and an Object array of the key/value pairs. This allows it to + * avoid having to create an extra object for every entry put in to the map, and it + * also tries to control the growth of the size of these arrays more aggressively + * (since growing them only requires copying the entries in the array, not rebuilding + * a hash map). + * + * <p>Note that this implementation is not intended to be appropriate for data structures + * that may contain large numbers of items. It is generally slower than a traditional + * HashMap, since lookups require a binary search and adds and removes require inserting + * and deleting entries in the array. For containers holding up to hundreds of items, + * the performance difference is not significant, less than 50%. For larger numbers of items + * this data structure should be avoided.</p> + * + * <p><b>Note:</b> unlike {@link java.util.HashMap}, this container does not support + * null keys.</p> + * + * <p>Because this container is intended to better balance memory use, unlike most other + * standard Java containers it will shrink its array as items are removed from it. Currently + * you have no control over this shrinking -- if you set a capacity and then remove an + * item, it may reduce the capacity to better match the current size. In the future an + * explicitly call to set the capacity should turn off this aggressive shrinking behavior.</p> + * + * @hide + */ +public final class ArrayMap<K, V> implements Map<K, V> { + private static final boolean DEBUG = false; + private static final String TAG = "ArrayMap"; + + /** + * The minimum amount by which the capacity of a ArrayMap will increase. + * This is tuned to be relatively space-efficient. + */ + private static final int BASE_SIZE = 4; + + /** + * Maximum number of entries to have in array caches. + */ + private static final int CACHE_SIZE = 10; + + /** + * Caches of small array objects to avoid spamming garbage. The cache + * Object[] variable is a pointer to a linked list of array objects. + * The first entry in the array is a pointer to the next array in the + * list; the second entry is a pointer to the int[] hash code array for it. + */ + static Object[] mBaseCache; + static int mBaseCacheSize; + static Object[] mTwiceBaseCache; + static int mTwiceBaseCacheSize; + + int[] mHashes; + Object[] mArray; + int mSize; + MapCollections<K, V> mCollections; + + private int indexOf(Object key, int hash) { + final int N = mSize; + + // Important fast case: if nothing is in here, nothing to look for. + if (N == 0) { + return ~0; + } + + int index = SparseArray.binarySearch(mHashes, N, hash); + + // If the hash code wasn't found, then we have no entry for this key. + if (index < 0) { + return index; + } + + // If the key at the returned index matches, that's what we want. + if (mArray[index<<1].equals(key)) { + return index; + } + + // Search for a matching key after the index. + int end; + for (end = index + 1; end < N && mHashes[end] == hash; end++) { + if (mArray[end << 1].equals(key)) return end; + } + + // Search for a matching key before the index. + for (int i = index - 1; i >= 0 && mHashes[i] == hash; i--) { + if (mArray[i << 1].equals(key)) return i; + } + + // Key not found -- return negative value indicating where a + // new entry for this key should go. We use the end of the + // hash chain to reduce the number of array entries that will + // need to be copied when inserting. + return ~end; + } + + private void allocArrays(final int size) { + if (size == (BASE_SIZE*2)) { + synchronized (ArrayMap.class) { + if (mTwiceBaseCache != null) { + final Object[] array = mTwiceBaseCache; + mArray = array; + mTwiceBaseCache = (Object[])array[0]; + mHashes = (int[])array[1]; + array[0] = array[1] = null; + mTwiceBaseCacheSize--; + if (DEBUG) Log.d(TAG, "Retrieving 2x cache " + mHashes + + " now have " + mTwiceBaseCacheSize + " entries"); + return; + } + } + } else if (size == BASE_SIZE) { + synchronized (ArrayMap.class) { + if (mBaseCache != null) { + final Object[] array = mBaseCache; + mArray = array; + mBaseCache = (Object[])array[0]; + mHashes = (int[])array[1]; + array[0] = array[1] = null; + mBaseCacheSize--; + if (DEBUG) Log.d(TAG, "Retrieving 1x cache " + mHashes + + " now have " + mBaseCacheSize + " entries"); + return; + } + } + } + + mHashes = new int[size]; + mArray = new Object[size<<1]; + } + + private static void freeArrays(final int[] hashes, final Object[] array, final int size) { + if (hashes.length == (BASE_SIZE*2)) { + synchronized (ArrayMap.class) { + if (mTwiceBaseCacheSize < CACHE_SIZE) { + array[0] = mTwiceBaseCache; + array[1] = hashes; + for (int i=(size<<1)-1; i>=2; i--) { + array[i] = null; + } + mTwiceBaseCache = array; + mTwiceBaseCacheSize++; + if (DEBUG) Log.d(TAG, "Storing 2x cache " + array + + " now have " + mTwiceBaseCacheSize + " entries"); + } + } + } else if (hashes.length == BASE_SIZE) { + synchronized (ArrayMap.class) { + if (mBaseCacheSize < CACHE_SIZE) { + array[0] = mBaseCache; + array[1] = hashes; + for (int i=(size<<1)-1; i>=2; i--) { + array[i] = null; + } + mBaseCache = array; + mBaseCacheSize++; + if (DEBUG) Log.d(TAG, "Storing 1x cache " + array + + " now have " + mBaseCacheSize + " entries"); + } + } + } + } + + /** + * Create a new empty ArrayMap. The default capacity of an array map is 0, and + * will grow once items are added to it. + */ + public ArrayMap() { + mHashes = SparseArray.EMPTY_INTS; + mArray = SparseArray.EMPTY_OBJECTS; + mSize = 0; + } + + /** + * Create a new ArrayMap with a given initial capacity. + */ + public ArrayMap(int capacity) { + if (capacity == 0) { + mHashes = SparseArray.EMPTY_INTS; + mArray = SparseArray.EMPTY_OBJECTS; + } else { + allocArrays(capacity); + } + mSize = 0; + } + + /** + * Make the array map empty. All storage is released. + */ + @Override + public void clear() { + freeArrays(mHashes, mArray, mSize); + mHashes = SparseArray.EMPTY_INTS; + mArray = SparseArray.EMPTY_OBJECTS; + mSize = 0; + } + + /** + * Ensure the array map can hold at least <var>minimumCapacity</var> + * items. + */ + public void ensureCapacity(int minimumCapacity) { + if (mHashes.length < minimumCapacity) { + int[] ohashes = mHashes; + Object[] oarray = mArray; + allocArrays(minimumCapacity); + if (mHashes.length > 0) { + System.arraycopy(ohashes, 0, mHashes, 0, mHashes.length); + System.arraycopy(oarray, 0, mArray, 0, mArray.length); + } + freeArrays(ohashes, oarray, mSize); + } + } + + /** + * Check whether a key exists in the array. + * + * @param key The key to search for. + * @return Returns true if the key exists, else false. + */ + @Override + public boolean containsKey(Object key) { + return indexOf(key, key.hashCode()) >= 0; + } + + private int indexOfValue(Object value) { + final int N = mSize*2; + final Object[] array = mArray; + if (value == null) { + for (int i=1; i<N; i+=2) { + if (array[i] == null) { + return i>>1; + } + } + } else { + for (int i=1; i<N; i+=2) { + if (value.equals(array[i])) { + return i>>1; + } + } + } + return -1; + } + + /** + * Check whether a value exists in the array. This requires a linear search + * through the entire array. + * + * @param value The value to search for. + * @return Returns true if the value exists, else false. + */ + @Override + public boolean containsValue(Object value) { + return indexOfValue(value) >= 0; + } + + /** + * Retrieve a value from the array. + * @param key The key of the value to retrieve. + * @return Returns the value associated with the given key, + * or null if there is no such key. + */ + @Override + public V get(Object key) { + final int index = indexOf(key, key.hashCode()); + return index >= 0 ? (V)mArray[(index<<1)+1] : null; + } + + /** + * Return the key at the given index in the array. + * @param index The desired index, must be between 0 and {@link #size()}-1. + * @return Returns the key stored at the given index. + */ + public K keyAt(int index) { + return (K)mArray[index << 1]; + } + + /** + * Return the value at the given index in the array. + * @param index The desired index, must be between 0 and {@link #size()}-1. + * @return Returns the value stored at the given index. + */ + public V valueAt(int index) { + return (V)mArray[(index << 1) + 1]; + } + + /** + * Set the value at a given index in the array. + * @param index The desired index, must be between 0 and {@link #size()}-1. + * @param value The new value to store at this index. + * @return Returns the previous value at the given index. + */ + public V setValueAt(int index, V value) { + index = (index << 1) + 1; + V old = (V)mArray[index]; + mArray[index] = value; + return old; + } + + /** + * Return true if the array map contains no items. + */ + @Override + public boolean isEmpty() { + return mSize <= 0; + } + + /** + * Add a new value to the array map. + * @param key The key under which to store the value. <b>Must not be null.</b> If + * this key already exists in the array, its value will be replaced. + * @param value The value to store for the given key. + * @return Returns the old value that was stored for the given key, or null if there + * was no such key. + */ + @Override + public V put(K key, V value) { + final int hash = key.hashCode(); + int index = indexOf(key, hash); + if (index >= 0) { + index = (index<<1) + 1; + final V old = (V)mArray[index]; + mArray[index] = value; + return old; + } + + index = ~index; + if (mSize >= mHashes.length) { + final int n = mSize >= (BASE_SIZE*2) ? (mSize+(mSize>>1)) + : (mSize >= BASE_SIZE ? (BASE_SIZE*2) : BASE_SIZE); + + if (DEBUG) Log.d(TAG, "put: grow from " + mHashes.length + " to " + n); + + final int[] ohashes = mHashes; + final Object[] oarray = mArray; + allocArrays(n); + + if (mHashes.length > 0) { + if (DEBUG) Log.d(TAG, "put: copy 0-" + mSize + " to 0"); + System.arraycopy(ohashes, 0, mHashes, 0, ohashes.length); + System.arraycopy(oarray, 0, mArray, 0, oarray.length); + } + + freeArrays(ohashes, oarray, mSize); + } + + if (index < mSize) { + if (DEBUG) Log.d(TAG, "put: move " + index + "-" + (mSize-index) + + " to " + (index+1)); + System.arraycopy(mHashes, index, mHashes, index + 1, mSize - index); + System.arraycopy(mArray, index << 1, mArray, (index + 1) << 1, (mSize - index) << 1); + } + + mHashes[index] = hash; + mArray[index<<1] = key; + mArray[(index<<1)+1] = value; + mSize++; + return null; + } + + /** + * Perform a {@link #put(Object, Object)} of all key/value pairs in <var>array</var> + * @param array The array whose contents are to be retrieved. + */ + public void putAll(ArrayMap<? extends K, ? extends V> array) { + final int N = array.mSize; + ensureCapacity(mSize + N); + for (int i=0; i<N; i++) { + put(array.keyAt(i), array.valueAt(i)); + } + } + + /** + * Remove an existing key from the array map. + * @param key The key of the mapping to remove. + * @return Returns the value that was stored under the key, or null if there + * was no such key. + */ + @Override + public V remove(Object key) { + int index = indexOf(key, key.hashCode()); + if (index >= 0) { + return removeAt(index); + } + + return null; + } + + /** + * Remove the key/value mapping at the given index. + * @param index The desired index, must be between 0 and {@link #size()}-1. + * @return Returns the value that was stored at this index. + */ + public V removeAt(int index) { + final V old = (V)mArray[(index << 1) + 1]; + if (mSize <= 1) { + // Now empty. + if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to 0"); + freeArrays(mHashes, mArray, mSize); + mHashes = SparseArray.EMPTY_INTS; + mArray = SparseArray.EMPTY_OBJECTS; + mSize = 0; + } else { + if (mHashes.length > (BASE_SIZE*2) && mSize < mHashes.length/3) { + // Shrunk enough to reduce size of arrays. We don't allow it to + // shrink smaller than (BASE_SIZE*2) to avoid flapping between + // that and BASE_SIZE. + final int n = mSize > (BASE_SIZE*2) ? (mSize + (mSize>>1)) : (BASE_SIZE*2); + + if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to " + n); + + final int[] ohashes = mHashes; + final Object[] oarray = mArray; + allocArrays(n); + + mSize--; + if (index > 0) { + if (DEBUG) Log.d(TAG, "remove: copy from 0-" + index + " to 0"); + System.arraycopy(ohashes, 0, mHashes, 0, index); + System.arraycopy(oarray, 0, mArray, 0, index << 1); + } + if (index < mSize) { + if (DEBUG) Log.d(TAG, "remove: copy from " + (index+1) + "-" + mSize + + " to " + index); + System.arraycopy(ohashes, index + 1, mHashes, index, mSize - index); + System.arraycopy(oarray, (index + 1) << 1, mArray, index << 1, + (mSize - index) << 1); + } + } else { + mSize--; + if (index < mSize) { + if (DEBUG) Log.d(TAG, "remove: move " + (index+1) + "-" + mSize + + " to " + index); + System.arraycopy(mHashes, index + 1, mHashes, index, mSize - index); + System.arraycopy(mArray, (index + 1) << 1, mArray, index << 1, + (mSize - index) << 1); + } + mArray[mSize << 1] = null; + mArray[(mSize << 1) + 1] = null; + } + } + return old; + } + + /** + * Return the number of items in this array map. + */ + @Override + public int size() { + return mSize; + } + + // ------------------------------------------------------------------------ + // Interop with traditional Java containers. Not as efficient as using + // specialized collection APIs. + // ------------------------------------------------------------------------ + + private MapCollections<K, V> getCollection() { + if (mCollections == null) { + mCollections = new MapCollections<K, V>() { + @Override + protected int colGetSize() { + return mSize; + } + + @Override + protected Object colGetEntry(int index, int offset) { + return mArray[(index<<1) + offset]; + } + + @Override + protected int colIndexOfKey(Object key) { + return indexOf(key, key.hashCode()); + } + + @Override + protected int colIndexOfValue(Object value) { + return indexOfValue(value); + } + + @Override + protected Map<K, V> colGetMap() { + return ArrayMap.this; + } + + @Override + protected void colPut(K key, V value) { + put(key, value); + } + + @Override + protected V colSetValue(int index, V value) { + return setValueAt(index, value); + } + + @Override + protected void colRemoveAt(int index) { + removeAt(index); + } + + @Override + protected void colClear() { + clear(); + } + }; + } + return mCollections; + } + + /** + * Determine if the array map contains all of the keys in the given collection. + * @param collection The collection whose contents are to be checked against. + * @return Returns true if this array map contains a key for every entry + * in <var>collection</var>, else returns false. + */ + public boolean containsAll(Collection<?> collection) { + return MapCollections.containsAllHelper(this, collection); + } + + /** + * Perform a {@link #put(Object, Object)} of all key/value pairs in <var>map</var> + * @param map The map whose contents are to be retrieved. + */ + @Override + public void putAll(Map<? extends K, ? extends V> map) { + ensureCapacity(mSize + map.size()); + for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + } + + /** + * Remove all keys in the array map that exist in the given collection. + * @param collection The collection whose contents are to be used to remove keys. + * @return Returns true if any keys were removed from the array map, else false. + */ + public boolean removeAll(Collection<?> collection) { + return MapCollections.removeAllHelper(this, collection); + } + + /** + * Remove all keys in the array map that do <b>not</b> exist in the given collection. + * @param collection The collection whose contents are to be used to determine which + * keys to keep. + * @return Returns true if any keys were removed from the array map, else false. + */ + public boolean retainAll(Collection<?> collection) { + return MapCollections.retainAllHelper(this, collection); + } + + /** + * Return a {@link java.util.Set} for iterating over and interacting with all mappings + * in the array map. + * + * <p><b>Note:</b> this is a very inefficient way to access the array contents, it + * requires generating a number of temporary objects.</p> + * + * <p><b>Note:</b></p> the semantics of this + * Set are subtly different than that of a {@link java.util.HashMap}: most important, + * the {@link java.util.Map.Entry Map.Entry} object returned by its iterator is a single + * object that exists for the entire iterator, so you can <b>not</b> hold on to it + * after calling {@link java.util.Iterator#next() Iterator.next}.</p> + */ + @Override + public Set<Map.Entry<K, V>> entrySet() { + return getCollection().getEntrySet(); + } + + /** + * Return a {@link java.util.Set} for iterating over and interacting with all keys + * in the array map. + * + * <p><b>Note:</b> this is a fair inefficient way to access the array contents, it + * requires generating a number of temporary objects.</p> + */ + @Override + public Set<K> keySet() { + return getCollection().getKeySet(); + } + + /** + * Return a {@link java.util.Collection} for iterating over and interacting with all values + * in the array map. + * + * <p><b>Note:</b> this is a fair inefficient way to access the array contents, it + * requires generating a number of temporary objects.</p> + */ + @Override + public Collection<V> values() { + return getCollection().getValues(); + } +} diff --git a/core/java/android/util/LongSparseArray.java b/core/java/android/util/LongSparseArray.java index 630e5f3..660b743 100644 --- a/core/java/android/util/LongSparseArray.java +++ b/core/java/android/util/LongSparseArray.java @@ -41,13 +41,19 @@ public class LongSparseArray<E> implements Cloneable { /** * Creates a new LongSparseArray containing no mappings that will not * require any additional memory allocation to store the specified - * number of mappings. + * number of mappings. If you supply an initial capacity of 0, the + * sparse array will be initialized with a light-weight representation + * not requiring any additional array allocations. */ public LongSparseArray(int initialCapacity) { - initialCapacity = ArrayUtils.idealLongArraySize(initialCapacity); - - mKeys = new long[initialCapacity]; - mValues = new Object[initialCapacity]; + if (initialCapacity == 0) { + mKeys = SparseLongArray.EMPTY_LONGS; + mValues = SparseArray.EMPTY_OBJECTS; + } else { + initialCapacity = ArrayUtils.idealLongArraySize(initialCapacity); + mKeys = new long[initialCapacity]; + mValues = new Object[initialCapacity]; + } mSize = 0; } diff --git a/core/java/android/util/LongSparseLongArray.java b/core/java/android/util/LongSparseLongArray.java index 34b6126..503295c 100644 --- a/core/java/android/util/LongSparseLongArray.java +++ b/core/java/android/util/LongSparseLongArray.java @@ -42,13 +42,19 @@ public class LongSparseLongArray implements Cloneable { /** * Creates a new SparseLongArray containing no mappings that will not * require any additional memory allocation to store the specified - * number of mappings. + * number of mappings. If you supply an initial capacity of 0, the + * sparse array will be initialized with a light-weight representation + * not requiring any additional array allocations. */ public LongSparseLongArray(int initialCapacity) { - initialCapacity = ArrayUtils.idealLongArraySize(initialCapacity); - - mKeys = new long[initialCapacity]; - mValues = new long[initialCapacity]; + if (initialCapacity == 0) { + mKeys = SparseLongArray.EMPTY_LONGS; + mValues = SparseLongArray.EMPTY_LONGS; + } else { + initialCapacity = ArrayUtils.idealLongArraySize(initialCapacity); + mKeys = new long[initialCapacity]; + mValues = new long[initialCapacity]; + } mSize = 0; } diff --git a/core/java/android/util/MapCollections.java b/core/java/android/util/MapCollections.java new file mode 100644 index 0000000..f29fb65 --- /dev/null +++ b/core/java/android/util/MapCollections.java @@ -0,0 +1,496 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util; + +import libcore.util.Objects; + +import java.lang.reflect.Array; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * Helper for writing standard Java collection interfaces to a data + * structure like {@link ArrayMap}. + * @hide + */ +abstract class MapCollections<K, V> { + EntrySet mEntrySet; + KeySet mKeySet; + ValuesCollection mValues; + + final class ArrayIterator<T> implements Iterator<T> { + final int mOffset; + int mSize; + int mIndex; + boolean mCanRemove = false; + + ArrayIterator(int offset) { + mOffset = offset; + mSize = colGetSize(); + } + + @Override + public boolean hasNext() { + return mIndex < mSize; + } + + @Override + public T next() { + Object res = colGetEntry(mIndex, mOffset); + mIndex++; + mCanRemove = true; + return (T)res; + } + + @Override + public void remove() { + if (!mCanRemove) { + throw new IllegalStateException(); + } + mIndex--; + mSize--; + mCanRemove = false; + colRemoveAt(mIndex); + } + } + + final class MapIterator implements Iterator<Map.Entry<K, V>>, Map.Entry<K, V> { + int mEnd; + int mIndex; + boolean mEntryValid = false; + + MapIterator() { + mEnd = colGetSize() - 1; + mIndex = -1; + } + + @Override + public boolean hasNext() { + return mIndex < mEnd; + } + + @Override + public Map.Entry<K, V> next() { + mIndex++; + mEntryValid = true; + return this; + } + + @Override + public void remove() { + if (!mEntryValid) { + throw new IllegalStateException(); + } + mIndex--; + mEnd--; + mEntryValid = false; + colRemoveAt(mIndex); + } + + @Override + public K getKey() { + if (!mEntryValid) { + throw new IllegalStateException( + "This container does not support retaining Map.Entry objects"); + } + return (K)colGetEntry(mIndex, 0); + } + + @Override + public V getValue() { + if (!mEntryValid) { + throw new IllegalStateException( + "This container does not support retaining Map.Entry objects"); + } + return (V)colGetEntry(mIndex, 1); + } + + @Override + public V setValue(V object) { + if (!mEntryValid) { + throw new IllegalStateException( + "This container does not support retaining Map.Entry objects"); + } + return colSetValue(mIndex, object); + } + + @Override + public final boolean equals(Object o) { + if (!mEntryValid) { + throw new IllegalStateException( + "This container does not support retaining Map.Entry objects"); + } + if (!(o instanceof Map.Entry)) { + return false; + } + Map.Entry<?, ?> e = (Map.Entry<?, ?>) o; + return Objects.equal(e.getKey(), colGetEntry(mIndex, 0)) + && Objects.equal(e.getValue(), colGetEntry(mIndex, 1)); + } + + @Override + public final int hashCode() { + if (!mEntryValid) { + throw new IllegalStateException( + "This container does not support retaining Map.Entry objects"); + } + final Object key = colGetEntry(mIndex, 0); + final Object value = colGetEntry(mIndex, 1); + return (key == null ? 0 : key.hashCode()) ^ + (value == null ? 0 : value.hashCode()); + } + + @Override + public final String toString() { + return getKey() + "=" + getValue(); + } + } + + final class EntrySet implements Set<Map.Entry<K, V>> { + @Override + public boolean add(Map.Entry<K, V> object) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(Collection<? extends Map.Entry<K, V>> collection) { + int oldSize = colGetSize(); + for (Map.Entry<K, V> entry : collection) { + colPut(entry.getKey(), entry.getValue()); + } + return oldSize != colGetSize(); + } + + @Override + public void clear() { + colClear(); + } + + @Override + public boolean contains(Object object) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean containsAll(Collection<?> collection) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isEmpty() { + return colGetSize() == 0; + } + + @Override + public Iterator<Map.Entry<K, V>> iterator() { + return new MapIterator(); + } + + @Override + public boolean remove(Object object) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeAll(Collection<?> collection) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean retainAll(Collection<?> collection) { + throw new UnsupportedOperationException(); + } + + @Override + public int size() { + return colGetSize(); + } + + @Override + public Object[] toArray() { + throw new UnsupportedOperationException(); + } + + @Override + public <T> T[] toArray(T[] array) { + throw new UnsupportedOperationException(); + } + }; + + final class KeySet implements Set<K> { + + @Override + public boolean add(K object) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(Collection<? extends K> collection) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + colClear(); + } + + @Override + public boolean contains(Object object) { + return colIndexOfKey(object) >= 0; + } + + @Override + public boolean containsAll(Collection<?> collection) { + return removeAllHelper(colGetMap(), collection); + } + + @Override + public boolean isEmpty() { + return colGetSize() == 0; + } + + @Override + public Iterator<K> iterator() { + return new ArrayIterator<K>(0); + } + + @Override + public boolean remove(Object object) { + int index = colIndexOfKey(object); + if (index >= 0) { + colRemoveAt(index); + return true; + } + return false; + } + + @Override + public boolean removeAll(Collection<?> collection) { + return removeAllHelper(colGetMap(), collection); + } + + @Override + public boolean retainAll(Collection<?> collection) { + return retainAllHelper(colGetMap(), collection); + } + + @Override + public int size() { + return colGetSize(); + } + + @Override + public Object[] toArray() { + return toArrayHelper(1); + } + + @Override + public <T> T[] toArray(T[] array) { + return toArrayHelper(array, 1); + } + }; + + final class ValuesCollection implements Collection<V> { + + @Override + public boolean add(V object) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(Collection<? extends V> collection) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + colClear(); + } + + @Override + public boolean contains(Object object) { + return colIndexOfValue(object) >= 0; + } + + @Override + public boolean containsAll(Collection<?> collection) { + Iterator<?> it = collection.iterator(); + while (it.hasNext()) { + if (!contains(it.next())) { + return false; + } + } + return true; + } + + @Override + public boolean isEmpty() { + return colGetSize() == 0; + } + + @Override + public Iterator<V> iterator() { + return new ArrayIterator<V>(1); + } + + @Override + public boolean remove(Object object) { + int index = colIndexOfValue(object); + if (index >= 0) { + colRemoveAt(index); + return true; + } + return false; + } + + @Override + public boolean removeAll(Collection<?> collection) { + int N = colGetSize(); + boolean changed = false; + for (int i=0; i<N; i++) { + Object cur = colGetEntry(i, 1); + if (collection.contains(cur)) { + colRemoveAt(i); + i--; + N--; + changed = true; + } + } + return changed; + } + + @Override + public boolean retainAll(Collection<?> collection) { + int N = colGetSize(); + boolean changed = false; + for (int i=0; i<N; i++) { + Object cur = colGetEntry(i, 1); + if (!collection.contains(cur)) { + colRemoveAt(i); + i--; + N--; + changed = true; + } + } + return changed; + } + + @Override + public int size() { + return colGetSize(); + } + + @Override + public Object[] toArray() { + return toArrayHelper(1); + } + + @Override + public <T> T[] toArray(T[] array) { + return toArrayHelper(array, 1); + } + }; + + public static <K, V> boolean containsAllHelper(Map<K, V> map, Collection<?> collection) { + Iterator<?> it = collection.iterator(); + while (it.hasNext()) { + if (!map.containsKey(it.next())) { + return false; + } + } + return true; + } + + public static <K, V> boolean removeAllHelper(Map<K, V> map, Collection<?> collection) { + int oldSize = map.size(); + Iterator<?> it = collection.iterator(); + while (it.hasNext()) { + map.remove(it.next()); + } + return oldSize != map.size(); + } + + public static <K, V> boolean retainAllHelper(Map<K, V> map, Collection<?> collection) { + int oldSize = map.size(); + Iterator<K> it = map.keySet().iterator(); + while (it.hasNext()) { + if (!collection.contains(it.next())) { + it.remove(); + } + } + return oldSize != map.size(); + } + + + public Object[] toArrayHelper(int offset) { + final int N = colGetSize(); + Object[] result = new Object[N]; + for (int i=0; i<N; i++) { + result[i] = colGetEntry(i, offset); + } + return result; + } + + public <T> T[] toArrayHelper(T[] array, int offset) { + final int N = colGetSize(); + if (array.length < N) { + @SuppressWarnings("unchecked") T[] newArray + = (T[]) Array.newInstance(array.getClass().getComponentType(), N); + array = newArray; + } + for (int i=0; i<N; i++) { + array[i] = (T)colGetEntry(i, offset); + } + if (array.length > N) { + array[N] = null; + } + return array; + } + + public Set<Map.Entry<K, V>> getEntrySet() { + if (mEntrySet == null) { + mEntrySet = new EntrySet(); + } + return mEntrySet; + } + + public Set<K> getKeySet() { + if (mKeySet == null) { + mKeySet = new KeySet(); + } + return mKeySet; + } + + public Collection<V> getValues() { + if (mValues == null) { + mValues = new ValuesCollection(); + } + return mValues; + } + + protected abstract int colGetSize(); + protected abstract Object colGetEntry(int index, int offset); + protected abstract int colIndexOfKey(Object key); + protected abstract int colIndexOfValue(Object key); + protected abstract Map<K, V> colGetMap(); + protected abstract void colPut(K key, V value); + protected abstract V colSetValue(int index, V value); + protected abstract void colRemoveAt(int index); + protected abstract void colClear(); +} diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java index 7e8fee5..001fc5b 100644 --- a/core/java/android/util/SparseArray.java +++ b/core/java/android/util/SparseArray.java @@ -25,6 +25,8 @@ import com.android.internal.util.ArrayUtils; */ public class SparseArray<E> implements Cloneable { private static final Object DELETED = new Object(); + static final int[] EMPTY_INTS = new int[0]; + static final Object[] EMPTY_OBJECTS = new Object[0]; private boolean mGarbage = false; private int[] mKeys; @@ -41,13 +43,19 @@ public class SparseArray<E> implements Cloneable { /** * Creates a new SparseArray containing no mappings that will not * require any additional memory allocation to store the specified - * number of mappings. + * number of mappings. If you supply an initial capacity of 0, the + * sparse array will be initialized with a light-weight representation + * not requiring any additional array allocations. */ public SparseArray(int initialCapacity) { - initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity); - - mKeys = new int[initialCapacity]; - mValues = new Object[initialCapacity]; + if (initialCapacity == 0) { + mKeys = EMPTY_INTS; + mValues = EMPTY_OBJECTS; + } else { + initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity); + mKeys = new int[initialCapacity]; + mValues = new Object[initialCapacity]; + } mSize = 0; } @@ -79,7 +87,7 @@ public class SparseArray<E> implements Cloneable { */ @SuppressWarnings("unchecked") public E get(int key, E valueIfKeyNotFound) { - int i = binarySearch(mKeys, 0, mSize, key); + int i = binarySearch(mKeys, mSize, key); if (i < 0 || mValues[i] == DELETED) { return valueIfKeyNotFound; @@ -92,7 +100,7 @@ public class SparseArray<E> implements Cloneable { * Removes the mapping from the specified key, if there was any. */ public void delete(int key) { - int i = binarySearch(mKeys, 0, mSize, key); + int i = binarySearch(mKeys, mSize, key); if (i >= 0) { if (mValues[i] != DELETED) { @@ -153,7 +161,7 @@ public class SparseArray<E> implements Cloneable { * was one. */ public void put(int key, E value) { - int i = binarySearch(mKeys, 0, mSize, key); + int i = binarySearch(mKeys, mSize, key); if (i >= 0) { mValues[i] = value; @@ -170,7 +178,7 @@ public class SparseArray<E> implements Cloneable { gc(); // Search again because indices may have changed. - i = ~binarySearch(mKeys, 0, mSize, key); + i = ~binarySearch(mKeys, mSize, key); } if (mSize >= mKeys.length) { @@ -261,7 +269,7 @@ public class SparseArray<E> implements Cloneable { gc(); } - return binarySearch(mKeys, 0, mSize, key); + return binarySearch(mKeys, mSize, key); } /** @@ -335,23 +343,23 @@ public class SparseArray<E> implements Cloneable { mSize = pos + 1; } - private static int binarySearch(int[] a, int start, int len, int key) { - int high = start + len, low = start - 1, guess; - - while (high - low > 1) { - guess = (high + low) / 2; - - if (a[guess] < key) - low = guess; - else - high = guess; + // This is Arrays.binarySearch(), but doesn't do any argument validation. + static int binarySearch(int[] array, int size, int value) { + int lo = 0; + int hi = size - 1; + + while (lo <= hi) { + int mid = (lo + hi) >>> 1; + int midVal = array[mid]; + + if (midVal < value) { + lo = mid + 1; + } else if (midVal > value) { + hi = mid - 1; + } else { + return mid; // value found + } } - - if (high == start + len) - return ~(start + len); - else if (a[high] == key) - return high; - else - return ~high; + return ~lo; // value not present } } diff --git a/core/java/android/util/SparseBooleanArray.java b/core/java/android/util/SparseBooleanArray.java index 76c47c6..73e3629 100644 --- a/core/java/android/util/SparseBooleanArray.java +++ b/core/java/android/util/SparseBooleanArray.java @@ -25,6 +25,8 @@ import com.android.internal.util.ArrayUtils; * than using a HashMap to map Integers to Booleans. */ public class SparseBooleanArray implements Cloneable { + static final boolean[] EMPTY_BOOLEANS = new boolean[0]; + /** * Creates a new SparseBooleanArray containing no mappings. */ @@ -35,13 +37,19 @@ public class SparseBooleanArray implements Cloneable { /** * Creates a new SparseBooleanArray containing no mappings that will not * require any additional memory allocation to store the specified - * number of mappings. + * number of mappings. If you supply an initial capacity of 0, the + * sparse array will be initialized with a light-weight representation + * not requiring any additional array allocations. */ public SparseBooleanArray(int initialCapacity) { - initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity); - - mKeys = new int[initialCapacity]; - mValues = new boolean[initialCapacity]; + if (initialCapacity == 0) { + mKeys = SparseArray.EMPTY_INTS; + mValues = EMPTY_BOOLEANS; + } else { + initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity); + mKeys = new int[initialCapacity]; + mValues = new boolean[initialCapacity]; + } mSize = 0; } @@ -71,7 +79,7 @@ public class SparseBooleanArray implements Cloneable { * if no such mapping has been made. */ public boolean get(int key, boolean valueIfKeyNotFound) { - int i = binarySearch(mKeys, 0, mSize, key); + int i = SparseArray.binarySearch(mKeys, mSize, key); if (i < 0) { return valueIfKeyNotFound; @@ -84,7 +92,7 @@ public class SparseBooleanArray implements Cloneable { * Removes the mapping from the specified key, if there was any. */ public void delete(int key) { - int i = binarySearch(mKeys, 0, mSize, key); + int i = SparseArray.binarySearch(mKeys, mSize, key); if (i >= 0) { System.arraycopy(mKeys, i + 1, mKeys, i, mSize - (i + 1)); @@ -99,7 +107,7 @@ public class SparseBooleanArray implements Cloneable { * was one. */ public void put(int key, boolean value) { - int i = binarySearch(mKeys, 0, mSize, key); + int i = SparseArray.binarySearch(mKeys, mSize, key); if (i >= 0) { mValues[i] = value; @@ -164,7 +172,7 @@ public class SparseBooleanArray implements Cloneable { * key is not mapped. */ public int indexOfKey(int key) { - return binarySearch(mKeys, 0, mSize, key); + return SparseArray.binarySearch(mKeys, mSize, key); } /** @@ -220,26 +228,6 @@ public class SparseBooleanArray implements Cloneable { mSize = pos + 1; } - private static int binarySearch(int[] a, int start, int len, int key) { - int high = start + len, low = start - 1, guess; - - while (high - low > 1) { - guess = (high + low) / 2; - - if (a[guess] < key) - low = guess; - else - high = guess; - } - - if (high == start + len) - return ~(start + len); - else if (a[high] == key) - return high; - else - return ~high; - } - private int[] mKeys; private boolean[] mValues; private int mSize; diff --git a/core/java/android/util/SparseIntArray.java b/core/java/android/util/SparseIntArray.java index 8d11177..122f7f5 100644 --- a/core/java/android/util/SparseIntArray.java +++ b/core/java/android/util/SparseIntArray.java @@ -24,7 +24,6 @@ import com.android.internal.util.ArrayUtils; * than using a HashMap to map Integers to Integers. */ public class SparseIntArray implements Cloneable { - private int[] mKeys; private int[] mValues; private int mSize; @@ -39,13 +38,19 @@ public class SparseIntArray implements Cloneable { /** * Creates a new SparseIntArray containing no mappings that will not * require any additional memory allocation to store the specified - * number of mappings. + * number of mappings. If you supply an initial capacity of 0, the + * sparse array will be initialized with a light-weight representation + * not requiring any additional array allocations. */ public SparseIntArray(int initialCapacity) { - initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity); - - mKeys = new int[initialCapacity]; - mValues = new int[initialCapacity]; + if (initialCapacity == 0) { + mKeys = SparseArray.EMPTY_INTS; + mValues = SparseArray.EMPTY_INTS; + } else { + initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity); + mKeys = new int[initialCapacity]; + mValues = new int[initialCapacity]; + } mSize = 0; } @@ -75,7 +80,7 @@ public class SparseIntArray implements Cloneable { * if no such mapping has been made. */ public int get(int key, int valueIfKeyNotFound) { - int i = binarySearch(mKeys, 0, mSize, key); + int i = SparseArray.binarySearch(mKeys, mSize, key); if (i < 0) { return valueIfKeyNotFound; @@ -88,7 +93,7 @@ public class SparseIntArray implements Cloneable { * Removes the mapping from the specified key, if there was any. */ public void delete(int key) { - int i = binarySearch(mKeys, 0, mSize, key); + int i = SparseArray.binarySearch(mKeys, mSize, key); if (i >= 0) { removeAt(i); @@ -110,7 +115,7 @@ public class SparseIntArray implements Cloneable { * was one. */ public void put(int key, int value) { - int i = binarySearch(mKeys, 0, mSize, key); + int i = SparseArray.binarySearch(mKeys, mSize, key); if (i >= 0) { mValues[i] = value; @@ -175,7 +180,7 @@ public class SparseIntArray implements Cloneable { * key is not mapped. */ public int indexOfKey(int key) { - return binarySearch(mKeys, 0, mSize, key); + return SparseArray.binarySearch(mKeys, mSize, key); } /** @@ -230,24 +235,4 @@ public class SparseIntArray implements Cloneable { mValues[pos] = value; mSize = pos + 1; } - - private static int binarySearch(int[] a, int start, int len, int key) { - int high = start + len, low = start - 1, guess; - - while (high - low > 1) { - guess = (high + low) / 2; - - if (a[guess] < key) - low = guess; - else - high = guess; - } - - if (high == start + len) - return ~(start + len); - else if (a[high] == key) - return high; - else - return ~high; - } } diff --git a/core/java/android/util/SparseLongArray.java b/core/java/android/util/SparseLongArray.java index 2f7a6fe..c608996 100644 --- a/core/java/android/util/SparseLongArray.java +++ b/core/java/android/util/SparseLongArray.java @@ -24,6 +24,7 @@ import com.android.internal.util.ArrayUtils; * than using a HashMap to map Integers to Longs. */ public class SparseLongArray implements Cloneable { + static final long[] EMPTY_LONGS = new long[0]; private int[] mKeys; private long[] mValues; @@ -39,13 +40,19 @@ public class SparseLongArray implements Cloneable { /** * Creates a new SparseLongArray containing no mappings that will not * require any additional memory allocation to store the specified - * number of mappings. + * number of mappings. If you supply an initial capacity of 0, the + * sparse array will be initialized with a light-weight representation + * not requiring any additional array allocations. */ public SparseLongArray(int initialCapacity) { - initialCapacity = ArrayUtils.idealLongArraySize(initialCapacity); - - mKeys = new int[initialCapacity]; - mValues = new long[initialCapacity]; + if (initialCapacity == 0) { + mKeys = SparseArray.EMPTY_INTS; + mValues = EMPTY_LONGS; + } else { + initialCapacity = ArrayUtils.idealLongArraySize(initialCapacity); + mKeys = new int[initialCapacity]; + mValues = new long[initialCapacity]; + } mSize = 0; } @@ -75,7 +82,7 @@ public class SparseLongArray implements Cloneable { * if no such mapping has been made. */ public long get(int key, long valueIfKeyNotFound) { - int i = binarySearch(mKeys, 0, mSize, key); + int i = SparseArray.binarySearch(mKeys, mSize, key); if (i < 0) { return valueIfKeyNotFound; @@ -88,7 +95,7 @@ public class SparseLongArray implements Cloneable { * Removes the mapping from the specified key, if there was any. */ public void delete(int key) { - int i = binarySearch(mKeys, 0, mSize, key); + int i = SparseArray.binarySearch(mKeys, mSize, key); if (i >= 0) { removeAt(i); @@ -110,7 +117,7 @@ public class SparseLongArray implements Cloneable { * was one. */ public void put(int key, long value) { - int i = binarySearch(mKeys, 0, mSize, key); + int i = SparseArray.binarySearch(mKeys, mSize, key); if (i >= 0) { mValues[i] = value; @@ -164,7 +171,7 @@ public class SparseLongArray implements Cloneable { * key is not mapped. */ public int indexOfKey(int key) { - return binarySearch(mKeys, 0, mSize, key); + return SparseArray.binarySearch(mKeys, mSize, key); } /** @@ -222,24 +229,4 @@ public class SparseLongArray implements Cloneable { mKeys = nkeys; mValues = nvalues; } - - private static int binarySearch(int[] a, int start, int len, long key) { - int high = start + len, low = start - 1, guess; - - while (high - low > 1) { - guess = (high + low) / 2; - - if (a[guess] < key) - low = guess; - else - high = guess; - } - - if (high == start + len) - return ~(start + len); - else if (a[high] == key) - return high; - else - return ~high; - } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index babccf6..d1fa455 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -15688,7 +15688,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private void setKeyedTag(int key, Object tag) { if (mKeyedTags == null) { - mKeyedTags = new SparseArray<Object>(); + mKeyedTags = new SparseArray<Object>(2); } mKeyedTags.put(key, tag); @@ -15966,7 +15966,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * <p>This mehod must be called by {@link #onMeasure(int, int)} to store the + * <p>This method must be called by {@link #onMeasure(int, int)} to store the * measured width and measured height. Failing to do so will trigger an * exception at measurement time.</p> * diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 077cb6f..b0fbe84 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -1517,9 +1517,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (lastHoverTarget != null) { lastHoverTarget.next = hoverTarget; } else { - lastHoverTarget = hoverTarget; mFirstHoverTarget = hoverTarget; } + lastHoverTarget = hoverTarget; // Dispatch the event to the child. if (action == MotionEvent.ACTION_HOVER_ENTER) { diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 5de7399..4c0ccca 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -1151,10 +1151,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } if (mChoiceMode != CHOICE_MODE_NONE) { if (mCheckStates == null) { - mCheckStates = new SparseBooleanArray(); + mCheckStates = new SparseBooleanArray(0); } if (mCheckedIdStates == null && mAdapter != null && mAdapter.hasStableIds()) { - mCheckedIdStates = new LongSparseArray<Integer>(); + mCheckedIdStates = new LongSparseArray<Integer>(0); } // Modal multi-choice mode only has choices when the mode is active. Clear them. if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) { diff --git a/core/java/android/widget/CalendarView.java b/core/java/android/widget/CalendarView.java index 361eca4..a19c6a8 100644 --- a/core/java/android/widget/CalendarView.java +++ b/core/java/android/widget/CalendarView.java @@ -247,7 +247,7 @@ public class CalendarView extends FrameLayout { /** * Which month should be displayed/highlighted [0-11]. */ - private int mCurrentMonthDisplayed; + private int mCurrentMonthDisplayed = -1; /** * Used for tracking during a scroll. diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java index 1d85126..aaa1adaa 100644 --- a/core/java/android/widget/Toast.java +++ b/core/java/android/widget/Toast.java @@ -29,6 +29,7 @@ import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; +import android.view.View.OnClickListener; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; @@ -74,6 +75,9 @@ public class Toast { */ public static final int LENGTH_LONG = 1; + /** @hide */ + public static final int LENGTH_INFINITE = 2; + final Context mContext; final TN mTN; int mDuration; @@ -288,6 +292,61 @@ public class Toast { tv.setText(s); } + /** @hide */ + public static Toast makeBar(Context context, int resId, int duration) { + return makeBar(context, context.getResources().getText(resId), duration); + } + + /** @hide */ + public static Toast makeBar(Context context, CharSequence text, int duration) { + Toast result = new Toast(context); + + LayoutInflater inflate = (LayoutInflater) + context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View v = inflate.inflate(com.android.internal.R.layout.toast_bar, null); + ((TextView)v.findViewById(android.R.id.message)).setText(text); + v.findViewById(android.R.id.button1).setVisibility(View.GONE); + + result.mNextView = v; + result.mDuration = duration; + result.mTN.mParams.alpha = 0.9f; + result.mTN.mParams.windowAnimations = com.android.internal.R.style.Animation_ToastBar; + + return result; + } + + /** @hide */ + public Toast setAction(int resId, Runnable action) { + return setAction(mContext.getResources().getText(resId), action); + } + + /** @hide */ + public Toast setAction(CharSequence actionText, final Runnable action) { + if (mNextView != null) { + TextView text1 = (TextView)mNextView.findViewById(android.R.id.text1); + View button1 = mNextView.findViewById(android.R.id.button1); + if (text1 != null && button1 != null) { + text1.setText(actionText); + button1.setVisibility(View.VISIBLE); + button1.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (action != null) { + action.run(); + } + }}); + return setInteractive(true); + } + } + throw new RuntimeException("This Toast was not created with Toast.makeBar()"); + } + + /** @hide */ + public Toast setInteractive(boolean interactive) { + mTN.setInteractive(interactive); + return this; + } + // ======================================================================================= // All the gunk below is the interaction with the Notification Service, which handles // the proper ordering of these system-wide. @@ -340,13 +399,20 @@ public class Toast { final WindowManager.LayoutParams params = mParams; params.height = WindowManager.LayoutParams.WRAP_CONTENT; params.width = WindowManager.LayoutParams.WRAP_CONTENT; - params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE - | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; params.format = PixelFormat.TRANSLUCENT; params.windowAnimations = com.android.internal.R.style.Animation_Toast; params.type = WindowManager.LayoutParams.TYPE_TOAST; params.setTitle("Toast"); + setInteractive(false); + } + + private void setInteractive(boolean interactive) { + mParams.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | (interactive + ? (WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH) + : WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); } /** |
