diff options
Diffstat (limited to 'core/java/android')
24 files changed, 991 insertions, 440 deletions
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 347850a..5e3dc02 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -534,17 +534,10 @@ public final class ActivityThread { private native void dumpGraphicsInfo(FileDescriptor fd); private class ApplicationThread extends ApplicationThreadNative { - private static final String HEAP_FULL_COLUMN - = "%13s %8s %8s %8s %8s %8s %8s %8s %8s %8s %8s"; - private static final String HEAP_COLUMN - = "%13s %8s %8s %8s %8s %8s %8s %8s"; private static final String ONE_COUNT_COLUMN = "%21s %8d"; private static final String TWO_COUNT_COLUMNS = "%21s %8d %21s %8d"; private static final String DB_INFO_FORMAT = " %8s %8s %14s %14s %s"; - // Formatting for checkin service - update version if row format changes - private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 3; - private int mLastProcessState = -1; private void updatePendingConfiguration(Configuration config) { @@ -929,82 +922,14 @@ public final class ActivityThread { long openSslSocketCount = Debug.countInstancesOfClass(OpenSSLSocketImpl.class); SQLiteDebug.PagerStats stats = SQLiteDebug.getDatabaseInfo(); - // For checkin, we print one long comma-separated list of values + dumpMemInfoTable(pw, memInfo, checkin, dumpFullInfo, dumpDalvik, Process.myPid(), + (mBoundApplication != null) ? mBoundApplication.processName : "unknown", + nativeMax, nativeAllocated, nativeFree, + dalvikMax, dalvikAllocated, dalvikFree); + if (checkin) { // NOTE: if you change anything significant below, also consider changing // ACTIVITY_THREAD_CHECKIN_VERSION. - String processName = (mBoundApplication != null) - ? mBoundApplication.processName : "unknown"; - - // Header - pw.print(ACTIVITY_THREAD_CHECKIN_VERSION); pw.print(','); - pw.print(Process.myPid()); pw.print(','); - pw.print(processName); pw.print(','); - - // Heap info - max - pw.print(nativeMax); pw.print(','); - pw.print(dalvikMax); pw.print(','); - pw.print("N/A,"); - pw.print(nativeMax + dalvikMax); pw.print(','); - - // Heap info - allocated - pw.print(nativeAllocated); pw.print(','); - pw.print(dalvikAllocated); pw.print(','); - pw.print("N/A,"); - pw.print(nativeAllocated + dalvikAllocated); pw.print(','); - - // Heap info - free - pw.print(nativeFree); pw.print(','); - pw.print(dalvikFree); pw.print(','); - pw.print("N/A,"); - pw.print(nativeFree + dalvikFree); pw.print(','); - - // Heap info - proportional set size - pw.print(memInfo.nativePss); pw.print(','); - pw.print(memInfo.dalvikPss); pw.print(','); - pw.print(memInfo.otherPss); pw.print(','); - pw.print(memInfo.getTotalPss()); pw.print(','); - - // Heap info - swappable set size - pw.print(memInfo.nativeSwappablePss); pw.print(','); - pw.print(memInfo.dalvikSwappablePss); pw.print(','); - pw.print(memInfo.otherSwappablePss); pw.print(','); - pw.print(memInfo.getTotalSwappablePss()); pw.print(','); - - // Heap info - shared dirty - pw.print(memInfo.nativeSharedDirty); pw.print(','); - pw.print(memInfo.dalvikSharedDirty); pw.print(','); - pw.print(memInfo.otherSharedDirty); pw.print(','); - pw.print(memInfo.getTotalSharedDirty()); pw.print(','); - - // Heap info - shared clean - pw.print(memInfo.nativeSharedClean); pw.print(','); - pw.print(memInfo.dalvikSharedClean); pw.print(','); - pw.print(memInfo.otherSharedClean); pw.print(','); - pw.print(memInfo.getTotalSharedClean()); pw.print(','); - - // Heap info - private Dirty - pw.print(memInfo.nativePrivateDirty); pw.print(','); - pw.print(memInfo.dalvikPrivateDirty); pw.print(','); - pw.print(memInfo.otherPrivateDirty); pw.print(','); - pw.print(memInfo.getTotalPrivateDirty()); pw.print(','); - - // Heap info - private Clean - pw.print(memInfo.nativePrivateClean); pw.print(','); - pw.print(memInfo.dalvikPrivateClean); pw.print(','); - pw.print(memInfo.otherPrivateClean); pw.print(','); - pw.print(memInfo.getTotalPrivateClean()); pw.print(','); - - // Heap info - other areas - for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_STATS; i++) { - pw.print(Debug.MemoryInfo.getOtherLabel(i)); pw.print(','); - pw.print(memInfo.getOtherPss(i)); pw.print(','); - pw.print(memInfo.getOtherSwappablePss(i)); pw.print(','); - pw.print(memInfo.getOtherSharedDirty(i)); pw.print(','); - pw.print(memInfo.getOtherSharedClean(i)); pw.print(','); - pw.print(memInfo.getOtherPrivateDirty(i)); pw.print(','); - pw.print(memInfo.getOtherPrivateClean(i)); pw.print(','); - } // Object counts pw.print(viewInstanceCount); pw.print(','); @@ -1039,128 +964,6 @@ public final class ActivityThread { return; } - // otherwise, show human-readable format - if (dumpFullInfo) { - printRow(pw, HEAP_FULL_COLUMN, "", "Pss", "Pss", "Shared", "Private", - "Shared", "Private", "Swapped", "Heap", "Heap", "Heap"); - printRow(pw, HEAP_FULL_COLUMN, "", "Total", "Clean", "Dirty", "Dirty", - "Clean", "Clean", "Dirty", "Size", "Alloc", "Free"); - printRow(pw, HEAP_FULL_COLUMN, "", "------", "------", "------", "------", - "------", "------", "------", "------", "------", "------"); - printRow(pw, HEAP_FULL_COLUMN, "Native Heap", memInfo.nativePss, - memInfo.nativeSwappablePss, memInfo.nativeSharedDirty, - memInfo.nativePrivateDirty, memInfo.nativeSharedClean, - memInfo.nativePrivateClean, memInfo.nativeSwappedOut, - nativeMax, nativeAllocated, nativeFree); - printRow(pw, HEAP_FULL_COLUMN, "Dalvik Heap", memInfo.dalvikPss, - memInfo.dalvikSwappablePss, memInfo.dalvikSharedDirty, - memInfo.dalvikPrivateDirty, memInfo.dalvikSharedClean, - memInfo.dalvikPrivateClean, memInfo.dalvikSwappedOut, - dalvikMax, dalvikAllocated, dalvikFree); - } else { - printRow(pw, HEAP_COLUMN, "", "Pss", "Private", - "Private", "Swapped", "Heap", "Heap", "Heap"); - printRow(pw, HEAP_COLUMN, "", "Total", "Dirty", - "Clean", "Dirty", "Size", "Alloc", "Free"); - printRow(pw, HEAP_COLUMN, "", "------", "------", "------", - "------", "------", "------", "------", "------"); - printRow(pw, HEAP_COLUMN, "Native Heap", memInfo.nativePss, - memInfo.nativePrivateDirty, - memInfo.nativePrivateClean, memInfo.nativeSwappedOut, - nativeMax, nativeAllocated, nativeFree); - printRow(pw, HEAP_COLUMN, "Dalvik Heap", memInfo.dalvikPss, - memInfo.dalvikPrivateDirty, - memInfo.dalvikPrivateClean, memInfo.dalvikSwappedOut, - dalvikMax, dalvikAllocated, dalvikFree); - } - - int otherPss = memInfo.otherPss; - int otherSwappablePss = memInfo.otherSwappablePss; - int otherSharedDirty = memInfo.otherSharedDirty; - int otherPrivateDirty = memInfo.otherPrivateDirty; - int otherSharedClean = memInfo.otherSharedClean; - int otherPrivateClean = memInfo.otherPrivateClean; - int otherSwappedOut = memInfo.otherSwappedOut; - - for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_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); - final int mySwappedOut = memInfo.getOtherSwappedOut(i); - if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0 - || mySharedClean != 0 || myPrivateClean != 0 || mySwappedOut != 0) { - if (dumpFullInfo) { - printRow(pw, HEAP_FULL_COLUMN, Debug.MemoryInfo.getOtherLabel(i), - myPss, mySwappablePss, mySharedDirty, myPrivateDirty, - mySharedClean, myPrivateClean, mySwappedOut, "", "", ""); - } else { - printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i), - myPss, myPrivateDirty, - myPrivateClean, mySwappedOut, "", "", ""); - } - otherPss -= myPss; - otherSwappablePss -= mySwappablePss; - otherSharedDirty -= mySharedDirty; - otherPrivateDirty -= myPrivateDirty; - otherSharedClean -= mySharedClean; - otherPrivateClean -= myPrivateClean; - otherSwappedOut -= mySwappedOut; - } - } - - if (dumpFullInfo) { - printRow(pw, HEAP_FULL_COLUMN, "Unknown", otherPss, otherSwappablePss, - otherSharedDirty, otherPrivateDirty, otherSharedClean, otherPrivateClean, - otherSwappedOut, "", "", ""); - printRow(pw, HEAP_FULL_COLUMN, "TOTAL", memInfo.getTotalPss(), - memInfo.getTotalSwappablePss(), - memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(), - memInfo.getTotalSharedClean(), memInfo.getTotalPrivateClean(), - memInfo.getTotalSwappedOut(), nativeMax+dalvikMax, - nativeAllocated+dalvikAllocated, nativeFree+dalvikFree); - } else { - printRow(pw, HEAP_COLUMN, "Unknown", otherPss, - otherPrivateDirty, otherPrivateClean, otherSwappedOut, - "", "", ""); - printRow(pw, HEAP_COLUMN, "TOTAL", memInfo.getTotalPss(), - memInfo.getTotalPrivateDirty(), - memInfo.getTotalPrivateClean(), - memInfo.getTotalSwappedOut(), - 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); - final int mySwappedOut = memInfo.getOtherSwappedOut(i); - if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0 - || mySharedClean != 0 || myPrivateClean != 0) { - if (dumpFullInfo) { - printRow(pw, HEAP_FULL_COLUMN, Debug.MemoryInfo.getOtherLabel(i), - myPss, mySwappablePss, mySharedDirty, myPrivateDirty, - mySharedClean, myPrivateClean, mySwappedOut, "", "", ""); - } else { - printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i), - myPss, myPrivateDirty, - myPrivateClean, mySwappedOut, "", "", ""); - } - } - } - } - pw.println(" "); pw.println(" Objects"); printRow(pw, TWO_COUNT_COLUMNS, "Views:", viewInstanceCount, "ViewRootImpl:", @@ -1238,10 +1041,6 @@ public final class ActivityThread { sendMessage(H.REQUEST_ASSIST_CONTEXT_EXTRAS, cmd); } - private void printRow(PrintWriter pw, String format, Object...objs) { - pw.println(String.format(format, objs)); - } - public void setCoreSettings(Bundle coreSettings) { sendMessage(H.SET_CORE_SETTINGS, coreSettings); } @@ -1959,6 +1758,223 @@ public final class ActivityThread { } } + private static final String HEAP_FULL_COLUMN + = "%13s %8s %8s %8s %8s %8s %8s %8s %8s %8s %8s"; + private static final String HEAP_COLUMN + = "%13s %8s %8s %8s %8s %8s %8s %8s"; + + // Formatting for checkin service - update version if row format changes + private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 3; + + static void printRow(PrintWriter pw, String format, Object...objs) { + pw.println(String.format(format, objs)); + } + + public static void dumpMemInfoTable(PrintWriter pw, Debug.MemoryInfo memInfo, boolean checkin, + boolean dumpFullInfo, boolean dumpDalvik, int pid, String processName, + long nativeMax, long nativeAllocated, long nativeFree, + long dalvikMax, long dalvikAllocated, long dalvikFree) { + + // For checkin, we print one long comma-separated list of values + if (checkin) { + // NOTE: if you change anything significant below, also consider changing + // ACTIVITY_THREAD_CHECKIN_VERSION. + + // Header + pw.print(ACTIVITY_THREAD_CHECKIN_VERSION); pw.print(','); + pw.print(pid); pw.print(','); + pw.print(processName); pw.print(','); + + // Heap info - max + pw.print(nativeMax); pw.print(','); + pw.print(dalvikMax); pw.print(','); + pw.print("N/A,"); + pw.print(nativeMax + dalvikMax); pw.print(','); + + // Heap info - allocated + pw.print(nativeAllocated); pw.print(','); + pw.print(dalvikAllocated); pw.print(','); + pw.print("N/A,"); + pw.print(nativeAllocated + dalvikAllocated); pw.print(','); + + // Heap info - free + pw.print(nativeFree); pw.print(','); + pw.print(dalvikFree); pw.print(','); + pw.print("N/A,"); + pw.print(nativeFree + dalvikFree); pw.print(','); + + // Heap info - proportional set size + pw.print(memInfo.nativePss); pw.print(','); + pw.print(memInfo.dalvikPss); pw.print(','); + pw.print(memInfo.otherPss); pw.print(','); + pw.print(memInfo.getTotalPss()); pw.print(','); + + // Heap info - swappable set size + pw.print(memInfo.nativeSwappablePss); pw.print(','); + pw.print(memInfo.dalvikSwappablePss); pw.print(','); + pw.print(memInfo.otherSwappablePss); pw.print(','); + pw.print(memInfo.getTotalSwappablePss()); pw.print(','); + + // Heap info - shared dirty + pw.print(memInfo.nativeSharedDirty); pw.print(','); + pw.print(memInfo.dalvikSharedDirty); pw.print(','); + pw.print(memInfo.otherSharedDirty); pw.print(','); + pw.print(memInfo.getTotalSharedDirty()); pw.print(','); + + // Heap info - shared clean + pw.print(memInfo.nativeSharedClean); pw.print(','); + pw.print(memInfo.dalvikSharedClean); pw.print(','); + pw.print(memInfo.otherSharedClean); pw.print(','); + pw.print(memInfo.getTotalSharedClean()); pw.print(','); + + // Heap info - private Dirty + pw.print(memInfo.nativePrivateDirty); pw.print(','); + pw.print(memInfo.dalvikPrivateDirty); pw.print(','); + pw.print(memInfo.otherPrivateDirty); pw.print(','); + pw.print(memInfo.getTotalPrivateDirty()); pw.print(','); + + // Heap info - private Clean + pw.print(memInfo.nativePrivateClean); pw.print(','); + pw.print(memInfo.dalvikPrivateClean); pw.print(','); + pw.print(memInfo.otherPrivateClean); pw.print(','); + pw.print(memInfo.getTotalPrivateClean()); pw.print(','); + + // Heap info - other areas + for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_STATS; i++) { + pw.print(Debug.MemoryInfo.getOtherLabel(i)); pw.print(','); + pw.print(memInfo.getOtherPss(i)); pw.print(','); + pw.print(memInfo.getOtherSwappablePss(i)); pw.print(','); + pw.print(memInfo.getOtherSharedDirty(i)); pw.print(','); + pw.print(memInfo.getOtherSharedClean(i)); pw.print(','); + pw.print(memInfo.getOtherPrivateDirty(i)); pw.print(','); + pw.print(memInfo.getOtherPrivateClean(i)); pw.print(','); + } + return; + } + + // otherwise, show human-readable format + if (dumpFullInfo) { + printRow(pw, HEAP_FULL_COLUMN, "", "Pss", "Pss", "Shared", "Private", + "Shared", "Private", "Swapped", "Heap", "Heap", "Heap"); + printRow(pw, HEAP_FULL_COLUMN, "", "Total", "Clean", "Dirty", "Dirty", + "Clean", "Clean", "Dirty", "Size", "Alloc", "Free"); + printRow(pw, HEAP_FULL_COLUMN, "", "------", "------", "------", "------", + "------", "------", "------", "------", "------", "------"); + printRow(pw, HEAP_FULL_COLUMN, "Native Heap", memInfo.nativePss, + memInfo.nativeSwappablePss, memInfo.nativeSharedDirty, + memInfo.nativePrivateDirty, memInfo.nativeSharedClean, + memInfo.nativePrivateClean, memInfo.nativeSwappedOut, + nativeMax, nativeAllocated, nativeFree); + printRow(pw, HEAP_FULL_COLUMN, "Dalvik Heap", memInfo.dalvikPss, + memInfo.dalvikSwappablePss, memInfo.dalvikSharedDirty, + memInfo.dalvikPrivateDirty, memInfo.dalvikSharedClean, + memInfo.dalvikPrivateClean, memInfo.dalvikSwappedOut, + dalvikMax, dalvikAllocated, dalvikFree); + } else { + printRow(pw, HEAP_COLUMN, "", "Pss", "Private", + "Private", "Swapped", "Heap", "Heap", "Heap"); + printRow(pw, HEAP_COLUMN, "", "Total", "Dirty", + "Clean", "Dirty", "Size", "Alloc", "Free"); + printRow(pw, HEAP_COLUMN, "", "------", "------", "------", + "------", "------", "------", "------", "------"); + printRow(pw, HEAP_COLUMN, "Native Heap", memInfo.nativePss, + memInfo.nativePrivateDirty, + memInfo.nativePrivateClean, memInfo.nativeSwappedOut, + nativeMax, nativeAllocated, nativeFree); + printRow(pw, HEAP_COLUMN, "Dalvik Heap", memInfo.dalvikPss, + memInfo.dalvikPrivateDirty, + memInfo.dalvikPrivateClean, memInfo.dalvikSwappedOut, + dalvikMax, dalvikAllocated, dalvikFree); + } + + int otherPss = memInfo.otherPss; + int otherSwappablePss = memInfo.otherSwappablePss; + int otherSharedDirty = memInfo.otherSharedDirty; + int otherPrivateDirty = memInfo.otherPrivateDirty; + int otherSharedClean = memInfo.otherSharedClean; + int otherPrivateClean = memInfo.otherPrivateClean; + int otherSwappedOut = memInfo.otherSwappedOut; + + for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_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); + final int mySwappedOut = memInfo.getOtherSwappedOut(i); + if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0 + || mySharedClean != 0 || myPrivateClean != 0 || mySwappedOut != 0) { + if (dumpFullInfo) { + printRow(pw, HEAP_FULL_COLUMN, Debug.MemoryInfo.getOtherLabel(i), + myPss, mySwappablePss, mySharedDirty, myPrivateDirty, + mySharedClean, myPrivateClean, mySwappedOut, "", "", ""); + } else { + printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i), + myPss, myPrivateDirty, + myPrivateClean, mySwappedOut, "", "", ""); + } + otherPss -= myPss; + otherSwappablePss -= mySwappablePss; + otherSharedDirty -= mySharedDirty; + otherPrivateDirty -= myPrivateDirty; + otherSharedClean -= mySharedClean; + otherPrivateClean -= myPrivateClean; + otherSwappedOut -= mySwappedOut; + } + } + + if (dumpFullInfo) { + printRow(pw, HEAP_FULL_COLUMN, "Unknown", otherPss, otherSwappablePss, + otherSharedDirty, otherPrivateDirty, otherSharedClean, otherPrivateClean, + otherSwappedOut, "", "", ""); + printRow(pw, HEAP_FULL_COLUMN, "TOTAL", memInfo.getTotalPss(), + memInfo.getTotalSwappablePss(), + memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(), + memInfo.getTotalSharedClean(), memInfo.getTotalPrivateClean(), + memInfo.getTotalSwappedOut(), nativeMax+dalvikMax, + nativeAllocated+dalvikAllocated, nativeFree+dalvikFree); + } else { + printRow(pw, HEAP_COLUMN, "Unknown", otherPss, + otherPrivateDirty, otherPrivateClean, otherSwappedOut, + "", "", ""); + printRow(pw, HEAP_COLUMN, "TOTAL", memInfo.getTotalPss(), + memInfo.getTotalPrivateDirty(), + memInfo.getTotalPrivateClean(), + memInfo.getTotalSwappedOut(), + 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); + final int mySwappedOut = memInfo.getOtherSwappedOut(i); + if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0 + || mySharedClean != 0 || myPrivateClean != 0) { + if (dumpFullInfo) { + printRow(pw, HEAP_FULL_COLUMN, Debug.MemoryInfo.getOtherLabel(i), + myPss, mySwappablePss, mySharedDirty, myPrivateDirty, + mySharedClean, myPrivateClean, mySwappedOut, "", "", ""); + } else { + printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i), + myPss, myPrivateDirty, + myPrivateClean, mySwappedOut, "", "", ""); + } + } + } + } + } + public void registerOnActivityPausedListener(Activity activity, OnActivityPausedListener listener) { synchronized (mOnPauseListeners) { diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java index 5c3a3e5..0cf7ad0 100644 --- a/core/java/android/app/AlarmManager.java +++ b/core/java/android/app/AlarmManager.java @@ -48,6 +48,15 @@ import android.os.WorkSource; * etc) it is easier and much more efficient to use * {@link android.os.Handler}.</b> * + * <p class="caution"><strong>Note:</strong> Beginning with API 19 + * ({@link android.os.Build.VERSION_CODES#KITKAT}) alarm delivery is inexact: + * the OS will shift alarms in order to minimize wakeups and battery use. There are + * new APIs to support applications which need strict delivery guarantees; see + * {@link #setWindow(int, long, long, PendingIntent)} and + * {@link #setExact(int, long, PendingIntent)}. Applications whose {@code targetSdkVersion} + * is earlier than API 19 will continue to see the previous behavior in which all + * alarms are delivered exactly when requested. + * * <p>You do not * instantiate this class directly; instead, retrieve it through * {@link android.content.Context#getSystemService @@ -109,21 +118,19 @@ public class AlarmManager } /** - * TBW: discussion of fuzzy nature of alarms in KLP+. - * * <p>Schedule an alarm. <b>Note: for timing operations (ticks, timeouts, - * etc) it is easier and much more efficient to use - * {@link android.os.Handler}.</b> If there is already an alarm scheduled - * for the same IntentSender, it will first be canceled. + * etc) it is easier and much more efficient to use {@link android.os.Handler}.</b> + * If there is already an alarm scheduled for the same IntentSender, that previous + * alarm will first be canceled. * - * <p>If the time occurs in the past, the alarm will be triggered + * <p>If the stated trigger time is in the past, the alarm will be triggered * immediately. If there is already an alarm for this Intent * scheduled (with the equality of two intents being defined by * {@link Intent#filterEquals}), then it will be removed and replaced by * this one. * * <p> - * The alarm is an intent broadcast that goes to a broadcast receiver that + * The alarm is an Intent broadcast that goes to a broadcast receiver that * you registered with {@link android.content.Context#registerReceiver} * or through the <receiver> tag in an AndroidManifest.xml file. * @@ -133,9 +140,34 @@ public class AlarmManager * how many past alarm events have been accumulated into this intent * broadcast. Recurring alarms that have gone undelivered because the * phone was asleep may have a count greater than one when delivered. - * - * @param type One of ELAPSED_REALTIME, ELAPSED_REALTIME_WAKEUP, RTC or - * RTC_WAKEUP. + * + * <div class="note"> + * <p> + * <b>Note:</b> Beginning in API 19, the trigger time passed to this method + * is treated as inexact: the alarm will not be delivered before this time, but + * may be deferred and delivered some time later. The OS will use + * this policy in order to "batch" alarms together across the entire system, + * minimizing the number of times the device needs to "wake up" and minimizing + * battery use. In general, alarms scheduled in the near future will not + * be deferred as long as alarms scheduled far in the future. + * + * <p> + * With the new batching policy, delivery ordering guarantees are not as + * strong as they were previously. If the application sets multiple alarms, + * it is possible that these alarms' <em>actual</em> delivery ordering may not match + * the order of their <em>requested</em> delivery times. If your application has + * strong ordering requirements there are other APIs that you can use to get + * the necessary behavior; see {@link #setWindow(int, long, long, PendingIntent)} + * and {@link #setExact(int, long, PendingIntent)}. + * + * <p> + * Applications whose {@code targetSdkVersion} is before API 19 will + * continue to get the previous alarm behavior: all of their scheduled alarms + * will be treated as exact. + * </div> + * + * @param type One of {@link #ELAPSED_REALTIME}, {@link #ELAPSED_REALTIME_WAKEUP}, + * {@link #RTC}, or {@link #RTC_WAKEUP}. * @param triggerAtMillis time in milliseconds that the alarm should go * off, using the appropriate clock (depending on the alarm type). * @param operation Action to perform when the alarm goes off; @@ -165,10 +197,10 @@ public class AlarmManager * {@link android.os.Handler}.</b> If there is already an alarm scheduled * for the same IntentSender, it will first be canceled. * - * <p>Like {@link #set}, except you can also - * supply a rate at which the alarm will repeat. This alarm continues - * repeating until explicitly removed with {@link #cancel}. If the time - * occurs in the past, the alarm will be triggered immediately, with an + * <p>Like {@link #set}, except you can also supply a period at which + * the alarm will automatically repeat. This alarm continues + * repeating until explicitly removed with {@link #cancel}. If the stated + * trigger time is in the past, the alarm will be triggered immediately, with an * alarm count depending on how far in the past the trigger time is relative * to the repeat interval. * @@ -185,8 +217,15 @@ public class AlarmManager * between alarms, then the approach to take is to use one-time alarms, * scheduling the next one yourself when handling each alarm delivery. * - * @param type One of ELAPSED_REALTIME, ELAPSED_REALTIME_WAKEUP}, RTC or - * RTC_WAKEUP. + * <p class="note"> + * <b>Note:</b> as of API 19, all repeating alarms are inexact. If your + * application needs precise delivery times then it must use one-time + * exact alarms, rescheduling each time as described above. Legacy applications + * whose {@code targetSdkVersion} is earlier than API 19 will continue to have all + * of their alarms, including repeating alarms, treated as exact. + * + * @param type One of {@link #ELAPSED_REALTIME}, {@link #ELAPSED_REALTIME_WAKEUP}, + * {@link #RTC}, or {@link #RTC_WAKEUP}. * @param triggerAtMillis time in milliseconds that the alarm should first * go off, using the appropriate clock (depending on the alarm type). * @param intervalMillis interval in milliseconds between subsequent repeats @@ -214,18 +253,33 @@ public class AlarmManager } /** - * Schedule an alarm to be delivered within a given window of time. + * Schedule an alarm to be delivered within a given window of time. This method + * is similar to {@link #set(int, long, PendingIntent)}, but allows the + * application to precisely control the degree to which its delivery might be + * adjusted by the OS. This method allows an application to take advantage of the + * battery optimizations that arise from delivery batching even when it has + * modest timeliness requirements for its alarms. * - * TBW: clean up these docs + * <p> + * This method can also be used to achieve strict ordering guarantees among + * multiple alarms by ensuring that the windows requested for each alarm do + * not intersect. + * + * <p> + * When precise delivery is not required, applications should use the standard + * {@link #set(int, long, PendingIntent)} method. This will give the OS the most + * flexibility to minimize wakeups and battery use. For alarms that must be delivered + * at precisely-specified times with no acceptable variation, applications can use + * {@link #setExact(int, long, PendingIntent)}. * - * @param type One of ELAPSED_REALTIME, ELAPSED_REALTIME_WAKEUP, RTC or - * RTC_WAKEUP. + * @param type One of {@link #ELAPSED_REALTIME}, {@link #ELAPSED_REALTIME_WAKEUP}, + * {@link #RTC}, or {@link #RTC_WAKEUP}. * @param windowStartMillis The earliest time, in milliseconds, that the alarm should * be delivered, expressed in the appropriate clock's units (depending on the alarm * type). * @param windowLengthMillis The length of the requested delivery window, * in milliseconds. The alarm will be delivered no later than this many - * milliseconds after the windowStartMillis time. Note that this parameter + * milliseconds after {@code windowStartMillis}. Note that this parameter * is a <i>duration,</i> not the timestamp of the end of the window. * @param operation Action to perform when the alarm goes off; * typically comes from {@link PendingIntent#getBroadcast @@ -249,8 +303,38 @@ public class AlarmManager } /** - * TBW: new 'exact' alarm that must be delivered as nearly as possible - * to the precise time specified. + * Schedule an alarm to be delivered precisely at the stated time. + * + * <p> + * This method is like {@link #set(int, long, PendingIntent)}, but does not permit + * the OS to adjust the delivery time. The alarm will be delivered as nearly as + * possible to the requested trigger time. + * + * <p> + * <b>Note:</b> only alarms for which there is a strong demand for exact-time + * delivery (such as an alarm clock ringing at the requested time) should be + * scheduled as exact. Applications are strongly discouraged from using exact + * alarms unnecessarily as they reduce the OS's ability to minimize battery use. + * + * @param type One of {@link #ELAPSED_REALTIME}, {@link #ELAPSED_REALTIME_WAKEUP}, + * {@link #RTC}, or {@link #RTC_WAKEUP}. + * @param triggerAtMillis time in milliseconds that the alarm should go + * off, using the appropriate clock (depending on the alarm type). + * @param operation Action to perform when the alarm goes off; + * typically comes from {@link PendingIntent#getBroadcast + * IntentSender.getBroadcast()}. + * + * @see #set + * @see #setRepeating + * @see #setWindow + * @see #cancel + * @see android.content.Context#sendBroadcast + * @see android.content.Context#registerReceiver + * @see android.content.Intent#filterEquals + * @see #ELAPSED_REALTIME + * @see #ELAPSED_REALTIME_WAKEUP + * @see #RTC + * @see #RTC_WAKEUP */ public void setExact(int type, long triggerAtMillis, PendingIntent operation) { setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, operation, null); @@ -283,74 +367,82 @@ public class AlarmManager } /** - * @deprecated setInexactRepeating() is deprecated; as of API 19 all - * repeating alarms are inexact. + * Available inexact recurrence interval recognized by + * {@link #setInexactRepeating(int, long, long, PendingIntent)} + * when running on Android prior to API 19. */ - @Deprecated public static final long INTERVAL_FIFTEEN_MINUTES = 15 * 60 * 1000; /** - * @deprecated setInexactRepeating() is deprecated; as of API 19 all - * repeating alarms are inexact. + * Available inexact recurrence interval recognized by + * {@link #setInexactRepeating(int, long, long, PendingIntent)} + * when running on Android prior to API 19. */ - @Deprecated public static final long INTERVAL_HALF_HOUR = 2*INTERVAL_FIFTEEN_MINUTES; /** - * @deprecated setInexactRepeating() is deprecated; as of API 19 all - * repeating alarms are inexact. + * Available inexact recurrence interval recognized by + * {@link #setInexactRepeating(int, long, long, PendingIntent)} + * when running on Android prior to API 19. */ - @Deprecated public static final long INTERVAL_HOUR = 2*INTERVAL_HALF_HOUR; /** - * @deprecated setInexactRepeating() is deprecated; as of API 19 all - * repeating alarms are inexact. + * Available inexact recurrence interval recognized by + * {@link #setInexactRepeating(int, long, long, PendingIntent)} + * when running on Android prior to API 19. */ - @Deprecated public static final long INTERVAL_HALF_DAY = 12*INTERVAL_HOUR; /** - * @deprecated setInexactRepeating() is deprecated; as of API 19 all - * repeating alarms are inexact. + * Available inexact recurrence interval recognized by + * {@link #setInexactRepeating(int, long, long, PendingIntent)} + * when running on Android prior to API 19. */ - @Deprecated public static final long INTERVAL_DAY = 2*INTERVAL_HALF_DAY; /** * Schedule a repeating alarm that has inexact trigger time requirements; * for example, an alarm that repeats every hour, but not necessarily at * the top of every hour. These alarms are more power-efficient than - * the strict recurrences supplied by {@link #setRepeating}, since the - * system can adjust alarms' phase to cause them to fire simultaneously, + * the strict recurrences traditionally supplied by {@link #setRepeating}, since the + * system can adjust alarms' delivery times to cause them to fire simultaneously, * avoiding waking the device from sleep more than necessary. - * + * * <p>Your alarm's first trigger will not be before the requested time, * but it might not occur for almost a full interval after that time. In * addition, while the overall period of the repeating alarm will be as * requested, the time between any two successive firings of the alarm * may vary. If your application demands very low jitter, use - * {@link #setRepeating} instead. + * one-shot alarms with an appropriate window instead; see {@link + * #setWindow(int, long, long, PendingIntent)} and + * {@link #setExact(int, long, PendingIntent)}. * - * @param type One of ELAPSED_REALTIME, ELAPSED_REALTIME_WAKEUP}, RTC or - * RTC_WAKEUP. + * <p class="note"> + * As of API 19, all repeating alarms are inexact. Because this method has + * been available since API 3, your application can safely call it and be + * assured that it will get similar behavior on both current and older versions + * of Android. + * + * @param type One of {@link #ELAPSED_REALTIME}, {@link #ELAPSED_REALTIME_WAKEUP}, + * {@link #RTC}, or {@link #RTC_WAKEUP}. * @param triggerAtMillis time in milliseconds that the alarm should first * go off, using the appropriate clock (depending on the alarm type). This * is inexact: the alarm will not fire before this time, but there may be a * delay of almost an entire alarm interval before the first invocation of * the alarm. * @param intervalMillis interval in milliseconds between subsequent repeats - * of the alarm. If this is one of INTERVAL_FIFTEEN_MINUTES, + * of the alarm. Prior to API 19, if this is one of INTERVAL_FIFTEEN_MINUTES, * INTERVAL_HALF_HOUR, INTERVAL_HOUR, INTERVAL_HALF_DAY, or INTERVAL_DAY * then the alarm will be phase-aligned with other alarms to reduce the * number of wakeups. Otherwise, the alarm will be set as though the - * application had called {@link #setRepeating}. + * application had called {@link #setRepeating}. As of API 19, all repeating + * alarms will be inexact and subject to batching with other alarms regardless + * of their stated repeat interval. * @param operation Action to perform when the alarm goes off; * typically comes from {@link PendingIntent#getBroadcast * IntentSender.getBroadcast()}. * - * @deprecated As of API 19, all repeating alarms are inexact. - * * @see android.os.Handler * @see #set * @see #cancel @@ -367,7 +459,6 @@ public class AlarmManager * @see #INTERVAL_HALF_DAY * @see #INTERVAL_DAY */ - @Deprecated public void setInexactRepeating(int type, long triggerAtMillis, long intervalMillis, PendingIntent operation) { setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, intervalMillis, operation, null); diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java index 5a919fb..f9c1d31 100644 --- a/core/java/android/os/Bundle.java +++ b/core/java/android/os/Bundle.java @@ -35,10 +35,12 @@ public final class Bundle implements Parcelable, Cloneable { public static final Bundle EMPTY; static final int BUNDLE_MAGIC = 0x4C444E42; // 'B' 'N' 'D' 'L' + static final Parcel EMPTY_PARCEL; static { EMPTY = new Bundle(); EMPTY.mMap = ArrayMap.EMPTY; + EMPTY_PARCEL = Parcel.obtain(); } // Invariant - exactly one of mMap / mParcelledData will be null @@ -115,9 +117,13 @@ public final class Bundle implements Parcelable, Cloneable { */ public Bundle(Bundle b) { if (b.mParcelledData != null) { - mParcelledData = Parcel.obtain(); - mParcelledData.appendFrom(b.mParcelledData, 0, b.mParcelledData.dataSize()); - mParcelledData.setDataPosition(0); + if (b.mParcelledData == EMPTY_PARCEL) { + mParcelledData = EMPTY_PARCEL; + } else { + mParcelledData = Parcel.obtain(); + mParcelledData.appendFrom(b.mParcelledData, 0, b.mParcelledData.dataSize()); + mParcelledData.setDataPosition(0); + } } else { mParcelledData = null; } @@ -216,6 +222,18 @@ public final class Bundle implements Parcelable, Cloneable { return; } + if (mParcelledData == EMPTY_PARCEL) { + if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this)) + + ": empty"); + if (mMap == null) { + mMap = new ArrayMap<String, Object>(1); + } else { + mMap.erase(); + } + mParcelledData = null; + return; + } + int N = mParcelledData.readInt(); if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this)) + ": reading " + N + " maps"); @@ -1652,11 +1670,20 @@ public final class Bundle implements Parcelable, Cloneable { final boolean oldAllowFds = parcel.pushAllowFds(mAllowFds); try { if (mParcelledData != null) { - int length = mParcelledData.dataSize(); - parcel.writeInt(length); - parcel.writeInt(BUNDLE_MAGIC); - parcel.appendFrom(mParcelledData, 0, length); + if (mParcelledData == EMPTY_PARCEL) { + parcel.writeInt(0); + } else { + int length = mParcelledData.dataSize(); + parcel.writeInt(length); + parcel.writeInt(BUNDLE_MAGIC); + parcel.appendFrom(mParcelledData, 0, length); + } } else { + // Special case for empty bundles. + if (mMap == null || mMap.size() <= 0) { + parcel.writeInt(0); + return; + } int lengthPos = parcel.dataPosition(); parcel.writeInt(-1); // dummy, will hold length parcel.writeInt(BUNDLE_MAGIC); @@ -1690,6 +1717,13 @@ public final class Bundle implements Parcelable, Cloneable { } void readFromParcelInner(Parcel parcel, int length) { + if (length == 0) { + // Empty Bundle or end of data. + mParcelledData = EMPTY_PARCEL; + mHasFds = false; + mFdsKnown = true; + return; + } int magic = parcel.readInt(); if (magic != BUNDLE_MAGIC) { //noinspection ThrowableInstanceNeverThrown @@ -1716,8 +1750,12 @@ public final class Bundle implements Parcelable, Cloneable { @Override public synchronized String toString() { if (mParcelledData != null) { - return "Bundle[mParcelledData.dataSize=" + - mParcelledData.dataSize() + "]"; + if (mParcelledData == EMPTY_PARCEL) { + return "Bundle[EMPTY_PARCEL]"; + } else { + return "Bundle[mParcelledData.dataSize=" + + mParcelledData.dataSize() + "]"; + } } return "Bundle[" + mMap.toString() + "]"; } diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index 4c7bbb4..56176a4 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -23,11 +23,12 @@ import android.os.WorkSource; interface IPowerManager { - // WARNING: The first three methods must remain the first three methods because their + // WARNING: The first four methods must remain the first three methods because their // transaction numbers must not change unless IPowerManager.cpp is also updated. void acquireWakeLock(IBinder lock, int flags, String tag, String packageName, in WorkSource ws); void acquireWakeLockWithUid(IBinder lock, int flags, String tag, String packageName, int uidtoblame); void releaseWakeLock(IBinder lock, int flags); + void updateWakeLockUids(IBinder lock, in int[] uids); void updateWakeLockWorkSource(IBinder lock, in WorkSource ws); boolean isWakeLockLevelSupported(int level); diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 02b1998..94b9617 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -611,11 +611,15 @@ public final class Parcel { here.fillInStackTrace(); Log.d(TAG, "Writing " + N + " ArrayMap entries", here); } + int startPos; for (int i=0; i<N; i++) { - if (DEBUG_ARRAY_MAP) Log.d(TAG, " Write #" + i + ": key=0x" - + (val.keyAt(i) != null ? val.keyAt(i).hashCode() : 0) + " " + val.keyAt(i)); + if (DEBUG_ARRAY_MAP) startPos = dataPosition(); writeValue(val.keyAt(i)); writeValue(val.valueAt(i)); + if (DEBUG_ARRAY_MAP) Log.d(TAG, " Write #" + i + " " + + (dataPosition()-startPos) + " bytes: key=0x" + + Integer.toHexString(val.keyAt(i) != null ? val.keyAt(i).hashCode() : 0) + + " " + val.keyAt(i)); } } @@ -2303,11 +2307,14 @@ public final class Parcel { here.fillInStackTrace(); Log.d(TAG, "Reading " + N + " ArrayMap entries", here); } + int startPos; while (N > 0) { + if (DEBUG_ARRAY_MAP) startPos = dataPosition(); Object key = readValue(loader); - if (DEBUG_ARRAY_MAP) Log.d(TAG, " Read #" + (N-1) + ": key=0x" - + (key != null ? key.hashCode() : 0) + " " + key); Object value = readValue(loader); + if (DEBUG_ARRAY_MAP) Log.d(TAG, " Read #" + (N-1) + " " + + (dataPosition()-startPos) + " bytes: key=0x" + + Integer.toHexString((key != null ? key.hashCode() : 0)) + " " + key); outVal.append(key, value); N--; } diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java index 1456387..5273c20 100644 --- a/core/java/android/os/ParcelFileDescriptor.java +++ b/core/java/android/os/ParcelFileDescriptor.java @@ -231,10 +231,11 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { final FileDescriptor fd = openInternal(file, mode); if (fd == null) return null; - final FileDescriptor[] comm = createCommSocketPair(true); + final FileDescriptor[] comm = createCommSocketPair(); final ParcelFileDescriptor pfd = new ParcelFileDescriptor(fd, comm[0]); // Kick off thread to watch for status updates + IoUtils.setBlocking(comm[1], true); final ListenerBridge bridge = new ListenerBridge(comm[1], handler.getLooper(), listener); bridge.start(); @@ -378,7 +379,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { */ public static ParcelFileDescriptor[] createReliablePipe() throws IOException { try { - final FileDescriptor[] comm = createCommSocketPair(false); + final FileDescriptor[] comm = createCommSocketPair(); final FileDescriptor[] fds = Libcore.os.pipe(); return new ParcelFileDescriptor[] { new ParcelFileDescriptor(fds[0], comm[0]), @@ -416,7 +417,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { */ public static ParcelFileDescriptor[] createReliableSocketPair() throws IOException { try { - final FileDescriptor[] comm = createCommSocketPair(false); + final FileDescriptor[] comm = createCommSocketPair(); final FileDescriptor fd0 = new FileDescriptor(); final FileDescriptor fd1 = new FileDescriptor(); Libcore.os.socketpair(AF_UNIX, SOCK_STREAM, 0, fd0, fd1); @@ -428,13 +429,13 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { } } - private static FileDescriptor[] createCommSocketPair(boolean blocking) throws IOException { + private static FileDescriptor[] createCommSocketPair() throws IOException { try { final FileDescriptor comm1 = new FileDescriptor(); final FileDescriptor comm2 = new FileDescriptor(); Libcore.os.socketpair(AF_UNIX, SOCK_STREAM, 0, comm1, comm2); - IoUtils.setBlocking(comm1, blocking); - IoUtils.setBlocking(comm2, blocking); + IoUtils.setBlocking(comm1, false); + IoUtils.setBlocking(comm2, false); return new FileDescriptor[] { comm1, comm2 }; } catch (ErrnoException e) { throw e.rethrowAsIOException(); @@ -670,34 +671,35 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { } try { - try { - if (status != Status.SILENCE) { - final byte[] buf = getOrCreateStatusBuffer(); - int writePtr = 0; + if (status == Status.SILENCE) return; + + // Since we're about to close, read off any remote status. It's + // okay to remember missing here. + mStatus = readCommStatus(mCommFd, getOrCreateStatusBuffer()); - Memory.pokeInt(buf, writePtr, status, ByteOrder.BIG_ENDIAN); - writePtr += 4; + // Skip writing status when other end has already gone away. + if (mStatus != null) return; + + try { + final byte[] buf = getOrCreateStatusBuffer(); + int writePtr = 0; - if (msg != null) { - final byte[] rawMsg = msg.getBytes(); - final int len = Math.min(rawMsg.length, buf.length - writePtr); - System.arraycopy(rawMsg, 0, buf, writePtr, len); - writePtr += len; - } + Memory.pokeInt(buf, writePtr, status, ByteOrder.BIG_ENDIAN); + writePtr += 4; - Libcore.os.write(mCommFd, buf, 0, writePtr); + if (msg != null) { + final byte[] rawMsg = msg.getBytes(); + final int len = Math.min(rawMsg.length, buf.length - writePtr); + System.arraycopy(rawMsg, 0, buf, writePtr, len); + writePtr += len; } + + Libcore.os.write(mCommFd, buf, 0, writePtr); } catch (ErrnoException e) { // Reporting status is best-effort Log.w(TAG, "Failed to report status: " + e); } - if (status != Status.SILENCE) { - // Since we're about to close, read off any remote status. It's - // okay to remember missing here. - mStatus = readCommStatus(mCommFd, getOrCreateStatusBuffer()); - } - } finally { IoUtils.closeQuietly(mCommFd); mCommFd = null; diff --git a/core/java/android/print/PageRange.java b/core/java/android/print/PageRange.java index cdcd0c7..d6320f0 100644 --- a/core/java/android/print/PageRange.java +++ b/core/java/android/print/PageRange.java @@ -39,9 +39,8 @@ public final class PageRange implements Parcelable { * @param start The start page index (zero based and inclusive). * @param end The end page index (zero based and inclusive). * - * @throws IllegalArgumentException If start is less than zero. - * @throws IllegalArgumentException If end is less than zero. - * @throws IllegalArgumentException If start greater than end. + * @throws IllegalArgumentException If start is less than zero or end + * is less than zero or start greater than end. */ public PageRange(int start, int end) { if (start < 0) { diff --git a/core/java/android/print/PrintAttributes.java b/core/java/android/print/PrintAttributes.java index e1a9cb7..c6254e0 100644 --- a/core/java/android/print/PrintAttributes.java +++ b/core/java/android/print/PrintAttributes.java @@ -30,7 +30,11 @@ import com.android.internal.R; import java.util.Map; /** - * This class represents the attributes of a print job. + * This class represents the attributes of a print job. These attributes + * describe how the printed content should be laid out. For example, the + * print attributes may state that the content should be laid out on a + * letter size with 300 DPI (dots per inch) resolution, have a margin of + * 10 mills (thousand of an inch) on all sides, and be black and white. */ public final class PrintAttributes implements Parcelable { /** Color mode: Monochrome color scheme, for example one color is used. */ @@ -277,7 +281,7 @@ public final class PrintAttributes implements Parcelable { * Unknown media size in portrait mode. * <p> * <strong>Note: </strong>This is for specifying orientation without media - * size. You should not use the dimensions reported by this class. + * size. You should not use the dimensions reported by this instance. * </p> */ public static final MediaSize UNKNOWN_PORTRAIT = @@ -288,7 +292,7 @@ public final class PrintAttributes implements Parcelable { * Unknown media size in landscape mode. * <p> * <strong>Note: </strong>This is for specifying orientation without media - * size. You should not use the dimensions reported by this class. + * size. You should not use the dimensions reported by this instance. * </p> */ public static final MediaSize UNKNOWN_LANDSCAPE = @@ -615,9 +619,7 @@ public final class PrintAttributes implements Parcelable { private final int mHeightMils; /** - * Creates a new instance. This is the preferred constructor since - * it enables the media size label to be shown in a localized fashion - * on a locale change. + * Creates a new instance. * * @param id The unique media size id. * @param packageName The name of the creating package. @@ -625,10 +627,9 @@ public final class PrintAttributes implements Parcelable { * @param widthMils The width in mils (thousands of an inch). * @param heightMils The height in mils (thousands of an inch). * - * @throws IllegalArgumentException If the id is empty. - * @throws IllegalArgumentException If the label is empty. - * @throws IllegalArgumentException If the widthMils is less than or equal to zero. - * @throws IllegalArgumentException If the heightMils is less than or equal to zero. + * @throws IllegalArgumentException If the id is empty or the label + * is empty or the widthMils is less than or equal to zero or the + * heightMils is less than or equal to zero. * * @hide */ @@ -667,14 +668,13 @@ public final class PrintAttributes implements Parcelable { * * @param id The unique media size id. It is unique amongst other media sizes * supported by the printer. - * @param label The <strong>internationalized</strong> human readable label. + * @param label The <strong>localized</strong> human readable label. * @param widthMils The width in mils (thousands of an inch). * @param heightMils The height in mils (thousands of an inch). * - * @throws IllegalArgumentException If the id is empty. - * @throws IllegalArgumentException If the label is empty. - * @throws IllegalArgumentException If the widthMils is less than or equal to zero. - * @throws IllegalArgumentException If the heightMils is less than or equal to zero. + * @throws IllegalArgumentException If the id is empty or the label is empty + * or the widthMils is less than or equal to zero or the heightMils is less + * than or equal to zero. */ public MediaSize(String id, String label, int widthMils, int heightMils) { if (TextUtils.isEmpty(id)) { @@ -776,12 +776,16 @@ public final class PrintAttributes implements Parcelable { } /** - * Returns a new media size in a portrait orientation + * Returns a new media size instance in a portrait orientation, * which is the height is the greater dimension. * - * @return New instance in landscape orientation. + * @return New instance in landscape orientation if this one + * is in landscape, otherwise this instance. */ public MediaSize asPortrait() { + if (isPortrait()) { + return this; + } return new MediaSize(mId, mLabel, mPackageName, Math.min(mWidthMils, mHeightMils), Math.max(mWidthMils, mHeightMils), @@ -789,12 +793,16 @@ public final class PrintAttributes implements Parcelable { } /** - * Returns a new media size in a landscape orientation + * Returns a new media size instance in a landscape orientation, * which is the height is the lesser dimension. * - * @return New instance in landscape orientation. + * @return New instance in landscape orientation if this one + * is in portrait, otherwise this instance. */ public MediaSize asLandscape() { + if (!isPortrait()) { + return this; + } return new MediaSize(mId, mLabel, mPackageName, Math.max(mWidthMils, mHeightMils), Math.min(mWidthMils, mHeightMils), @@ -881,8 +889,8 @@ public final class PrintAttributes implements Parcelable { * This class specifies a supported resolution in DPI (dots per inch). * Resolution defines how many points with different color can be placed * on one inch in horizontal or vertical direction of the target media. - * For example, a printer with 600DIP can produce higher quality images - * the one with 300DPI resolution. + * For example, a printer with 600 DPI can produce higher quality images + * the one with 300 DPI resolution. */ public static final class Resolution { private final String mId; @@ -895,14 +903,13 @@ public final class PrintAttributes implements Parcelable { * * @param id The unique resolution id. It is unique amongst other resolutions * supported by the printer. - * @param label The <strong>internationalized</strong> human readable label. + * @param label The <strong>localized</strong> human readable label. * @param horizontalDpi The horizontal resolution in DPI (dots per inch). * @param verticalDpi The vertical resolution in DPI (dots per inch). * - * @throws IllegalArgumentException If the id is empty. - * @throws IllegalArgumentException If the label is empty. - * @throws IllegalArgumentException If the horizontalDpi is less than or equal to zero. - * @throws IllegalArgumentException If the verticalDpi is less than or equal to zero. + * @throws IllegalArgumentException If the id is empty or the label is empty + * or the horizontalDpi is less than or equal to zero or the verticalDpi is + * less than or equal to zero. */ public Resolution(String id, String label, int horizontalDpi, int verticalDpi) { if (TextUtils.isEmpty(id)) { diff --git a/core/java/android/print/PrintDocumentAdapter.java b/core/java/android/print/PrintDocumentAdapter.java index 4113ac7..9e811a6 100644 --- a/core/java/android/print/PrintDocumentAdapter.java +++ b/core/java/android/print/PrintDocumentAdapter.java @@ -38,15 +38,46 @@ import android.os.ParcelFileDescriptor; * </li> * <li> * After every call to {@link #onLayout(PrintAttributes, PrintAttributes, - * CancellationSignal, LayoutResultCallback, Bundle)}, you may get a call to - * {@link #onWrite(PageRange[], ParcelFileDescriptor, CancellationSignal, WriteResultCallback)} - * asking you to write a PDF file with the content for specific pages. + * CancellationSignal, LayoutResultCallback, Bundle)}, you <strong>may</strong> get + * a call to {@link #onWrite(PageRange[], ParcelFileDescriptor, CancellationSignal, + * WriteResultCallback)} asking you to write a PDF file with the content for + * specific pages. * </li> * <li> * Finally, you will receive a call to {@link #onFinish()}. You can use this * callback to release resources allocated in {@link #onStart()}. * </li> * </ul> + * <p> + * The {@link #onStart()} callback is always the first call you will receive and + * is useful for doing one time setup or resource allocation before printing. You + * will not receive a subsequent call here. + * </p> + * <p> + * The {@link #onLayout(PrintAttributes, PrintAttributes, CancellationSignal, + * LayoutResultCallback, Bundle)} callback requires that you layout the content + * based on the current {@link PrintAttributes}. The execution of this method is + * not considered completed until you invoke one of the methods on the passed in + * callback instance. Hence, you will not receive a subsequent call to any other + * method of this class until the execution of this method is complete by invoking + * one of the callback methods. + * </p> + * <p> + * The {@link #onWrite(PageRange[], ParcelFileDescriptor, CancellationSignal, + * WriteResultCallback)} requires that you render and write the content of some + * pages to the provided destination. The execution of this method is not + * considered complete until you invoke one of the methods on the passed in + * callback instance. Hence, you will not receive a subsequent call to any other + * method of this class until the execution of this method is complete by invoking + * one of the callback methods. You will never receive a sequence of one or more + * calls to this method without a previous call to {@link #onLayout(PrintAttributes, + * PrintAttributes, CancellationSignal, LayoutResultCallback, Bundle)}. + * </p> + * <p> + * The {@link #onFinish()} callback is always the last call you will receive and + * is useful for doing one time cleanup or resource deallocation after printing. + * You will not receive a subsequent call here. + * </p> * </p> * <h3>Implementation</h3> * <p> @@ -54,7 +85,11 @@ import android.os.ParcelFileDescriptor; * of the work on an arbitrary thread. For example, if the printed content * does not depend on the UI state, i.e. on what is shown on the screen, then * you can offload the entire work on a dedicated thread, thus making your - * application interactive while the print work is being performed. + * application interactive while the print work is being performed. Note that + * while your activity is covered by the system print UI and a user cannot + * interact with it, doing the printing work on the main application thread + * may affect the performance of your other application components as they + * are also executed on that thread. * </p> * <p> * You can also do work on different threads, for example if you print UI @@ -64,7 +99,7 @@ import android.os.ParcelFileDescriptor; * This will ensure that the UI does not change while you are laying out the * printed content. Then you can handle {@link #onWrite(PageRange[], ParcelFileDescriptor, * CancellationSignal, WriteResultCallback)} and {@link #onFinish()} on another - * thread. This will ensure that the UI is frozen for the minimal amount of + * thread. This will ensure that the main thread is busy for a minimal amount of * time. Also this assumes that you will generate the printed content in * {@link #onLayout(PrintAttributes, PrintAttributes, CancellationSignal, * LayoutResultCallback, Bundle)} which is not mandatory. If you use multiple @@ -76,6 +111,12 @@ public abstract class PrintDocumentAdapter { /** * Extra: mapped to a boolean value that is <code>true</code> if * the current layout is for a print preview, <code>false</code> otherwise. + * This extra is provided in the {@link Bundle} argument of the {@link + * #onLayout(PrintAttributes, PrintAttributes, CancellationSignal, + * LayoutResultCallback, Bundle)} callback. + * + * @see #onLayout(PrintAttributes, PrintAttributes, CancellationSignal, + * LayoutResultCallback, Bundle) */ public static final String EXTRA_PRINT_PREVIEW = "EXTRA_PRINT_PREVIEW"; @@ -95,17 +136,20 @@ public abstract class PrintDocumentAdapter { * After you are done laying out, you <strong>must</strong> invoke: {@link * LayoutResultCallback#onLayoutFinished(PrintDocumentInfo, boolean)} with * the last argument <code>true</code> or <code>false</code> depending on - * whether the layout changed the content or not, respectively; and {@link - * LayoutResultCallback#onLayoutFailed(CharSequence)}, if an error occurred. - * Note that you must call one of the methods of the given callback. + * whether the layout changed the content or not, respectively; or {@link + * LayoutResultCallback#onLayoutFailed(CharSequence)}, if an error occurred; + * or {@link LayoutResultCallback#onLayoutCancelled()} if layout was + * cancelled in a response to a cancellation request via the passed in + * {@link CancellationSignal}. Note that you <strong>must</strong> call one of + * the methods of the given callback for this method to be considered complete. * </p> * <p> * <strong>Note:</strong> If the content is large and a layout will be * performed, it is a good practice to schedule the work on a dedicated * thread and register an observer in the provided {@link * CancellationSignal} upon invocation of which you should stop the - * layout. The cancellation callback will not be made on the main - * thread. + * layout. The cancellation callback <strong>will not</strong> be made on + * the main thread. * </p> * * @param oldAttributes The old print attributes. @@ -128,10 +172,12 @@ public abstract class PrintDocumentAdapter { * on the main thread. *<p> * After you are done writing, you should close the file descriptor and - * invoke {@link WriteResultCallback #onWriteFinished(PageRange[]), if writing + * invoke {@link WriteResultCallback#onWriteFinished(PageRange[])}, if writing * completed successfully; or {@link WriteResultCallback#onWriteFailed( - * CharSequence)}, if an error occurred. Note that you must call one of - * the methods of the given callback. + * CharSequence)}, if an error occurred; or {@link WriteResultCallback#onWriteCancelled()}, + * if writing was cancelled in a response to a cancellation request via the passed + * in {@link CancellationSignal}. Note that you <strong>must</strong> call one of + * the methods of the given callback for this method to be considered complete. * </p> * <p> * <strong>Note:</strong> If the printed content is large, it is a good @@ -178,7 +224,8 @@ public abstract class PrintDocumentAdapter { /** * Notifies that all the data was written. * - * @param pages The pages that were written. Cannot be null or empty. + * @param pages The pages that were written. Cannot be <code>null</code> + * or empty. */ public void onWriteFinished(PageRange[] pages) { /* do nothing - stub */ @@ -187,7 +234,8 @@ public abstract class PrintDocumentAdapter { /** * Notifies that an error occurred while writing the data. * - * @param error Error message. May be null if error is unknown. + * @param error The <strong>localized</strong> error message. + * shown to the user. May be <code>null</code> if error is unknown. */ public void onWriteFailed(CharSequence error) { /* do nothing - stub */ @@ -218,7 +266,7 @@ public abstract class PrintDocumentAdapter { /** * Notifies that the layout finished and whether the content changed. * - * @param info An info object describing the document. Cannot be null. + * @param info An info object describing the document. Cannot be <code>null</code>. * @param changed Whether the layout changed. * * @see PrintDocumentInfo @@ -230,7 +278,8 @@ public abstract class PrintDocumentAdapter { /** * Notifies that an error occurred while laying out the document. * - * @param error Error message. May be null if error is unknown. + * @param error The <strong>localized</strong> error message. + * shown to the user. May be <code>null</code> if error is unknown. */ public void onLayoutFailed(CharSequence error) { /* do nothing - stub */ diff --git a/core/java/android/print/PrintDocumentInfo.java b/core/java/android/print/PrintDocumentInfo.java index b721ef4..928be6c 100644 --- a/core/java/android/print/PrintDocumentInfo.java +++ b/core/java/android/print/PrintDocumentInfo.java @@ -21,12 +21,56 @@ import android.os.Parcelable; import android.text.TextUtils; /** - * This class encapsulates information about a printed document. + * This class encapsulates information about a document for printing + * purposes. This meta-data is used by the platform and print services, + * components that interact with printers. For example, this class + * contains the number of pages contained in the document it describes and + * this number of pages is shown to the user allowing him/her to select + * the range to print. Also a print service may optimize the printing + * process based on the content type, such as document or photo. + * <p> + * Instances of this class are created by the printing application and + * passed to the {@link PrintDocumentAdapter.LayoutResultCallback#onLayoutFinished( + * PrintDocumentInfo, boolean) PrintDocumentAdapter.LayoutResultCallback.onLayoutFinished( + * PrintDocumentInfo, boolean)} callback after successfully laying out the + * content which is performed in {@link PrintDocumentAdapter#onLayout(PrintAttributes, + * PrintAttributes, android.os.CancellationSignal, PrintDocumentAdapter.LayoutResultCallback, + * android.os.Bundle) PrintDocumentAdapter.onLayout(PrintAttributes, + * PrintAttributes, android.os.CancellationSignal, + * PrintDocumentAdapter.LayoutResultCallback, android.os.Bundle)}. + * </p> + * <p> + * An example usage looks like this: + * <pre> + * + * . . . + * + * public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes, + * CancellationSignal cancellationSignal, LayoutResultCallback callback, + * Bundle metadata) { + * + * // Assume the app defined a LayoutResult class which contains + * // the layout result data and that the content is a document. + * LayoutResult result = doSomeLayoutWork(); + * + * PrintDocumentInfo info = new PrintDocumentInfo + * .Builder("printed_file.pdf") + * .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT) + * .setPageCount(result.getPageCount()) + * .build(); + * + * callback.onLayoutFinished(info, result.getContentChanged()); + * } + * + * . . . + * + * </pre> + * </p> */ public final class PrintDocumentInfo implements Parcelable { /** - * Constant for unknown page count.. + * Constant for unknown page count. */ public static final int PAGE_COUNT_UNKNOWN = -1; @@ -37,11 +81,23 @@ public final class PrintDocumentInfo implements Parcelable { /** * Content type: document. + * <p> + * A print service may use normal paper to print the content instead + * of dedicated photo paper. Also it may use a lower quality printing + * process as the content is not as sensitive to print quality variation + * as a photo is. + * </p> */ public static final int CONTENT_TYPE_DOCUMENT = 0; /** * Content type: photo. + * <p> + * A print service may use dedicated photo paper to print the content + * instead of normal paper. Also it may use a higher quality printing + * process as the content is more sensitive to print quality variation + * than a document. + * </p> */ public static final int CONTENT_TYPE_PHOTO = 1; @@ -82,7 +138,8 @@ public final class PrintDocumentInfo implements Parcelable { } /** - * Gets the document name. + * Gets the document name. This name may be shown to + * the user. * * @return The document name. */ @@ -213,20 +270,23 @@ public final class PrintDocumentInfo implements Parcelable { } /** - * Builder for creating an {@link PrintDocumentInfo}. + * Builder for creating a {@link PrintDocumentInfo}. */ public static final class Builder { private final PrintDocumentInfo mPrototype; /** * Constructor. + * * <p> - * The values of the relevant properties are initialized with default - * values. Please refer to the documentation of the individual setters - * for information about the default values. + * The values of the relevant properties are initialized with defaults. + * Please refer to the documentation of the individual setters for + * information about the default values. * </p> * - * @param name The document name. Cannot be empty. + * @param name The document name which may be shown to the user and + * is the file name if the content it describes is saved as a PDF. + * Cannot be empty. */ public Builder(String name) { if (TextUtils.isEmpty(name)) { diff --git a/core/java/android/print/PrintJob.java b/core/java/android/print/PrintJob.java index 535ae43..0abe219 100644 --- a/core/java/android/print/PrintJob.java +++ b/core/java/android/print/PrintJob.java @@ -17,8 +17,13 @@ package android.print; /** - * This class represents a print job from the perspective of - * an application. + * This class represents a print job from the perspective of an + * application. It contains behavior methods for performing operations + * on it as well as methods for querying its state. A snapshot of the + * print job state is represented by the {@link PrintJobInfo} class. + * The state of a print job may change over time. An application receives + * instances of this class when creating a print job or querying for + * its print jobs. */ public final class PrintJob { @@ -145,11 +150,12 @@ public final class PrintJob { /** * Gets whether this print job is failed. Such a print job is * not successfully printed due to an error. You can request - * a restart via {@link #restart()}. + * a restart via {@link #restart()} or cancel via {@link #cancel()}. * * @return Whether the print job is failed. * * @see #restart() + * @see #cancel() */ public boolean isFailed() { return getInfo().getState() == PrintJobInfo.STATE_FAILED; diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java index c6f0a68..63f94fe 100644 --- a/core/java/android/print/PrintJobInfo.java +++ b/core/java/android/print/PrintJobInfo.java @@ -16,13 +16,17 @@ package android.print; +import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import java.util.Arrays; /** - * This class represents the description of a print job. + * This class represents the description of a print job. The print job + * state includes properties such as its id, print attributes used for + * generating the content, and so on. Note that the print jobs state may + * change over time and this class represents a snapshot of this state. */ public final class PrintJobInfo implements Parcelable { @@ -93,7 +97,7 @@ public final class PrintJobInfo implements Parcelable { public static final int STATE_BLOCKED = 4; /** - * Print job state: The print job was successfully printed. + * Print job state: The print job is successfully printed. * This is a terminal state. * <p> * Next valid states: None @@ -103,15 +107,14 @@ public final class PrintJobInfo implements Parcelable { /** * Print job state: The print job was printing but printing failed. - * This is a terminal state. * <p> - * Next valid states: None + * Next valid states: {@link #STATE_CANCELED}, {@link #STATE_STARTED} * </p> */ public static final int STATE_FAILED = 6; /** - * Print job state: The print job was canceled. + * Print job state: The print job is canceled. * This is a terminal state. * <p> * Next valid states: None @@ -158,6 +161,9 @@ public final class PrintJobInfo implements Parcelable { /** Information about the printed document. */ private PrintDocumentInfo mDocumentInfo; + /** Advanced printer specific options. */ + private Bundle mAdvancedOptions; + /** Whether we are trying to cancel this print job. */ private boolean mCanceling; @@ -182,6 +188,7 @@ public final class PrintJobInfo implements Parcelable { mAttributes = other.mAttributes; mDocumentInfo = other.mDocumentInfo; mCanceling = other.mCanceling; + mAdvancedOptions = other.mAdvancedOptions; } private PrintJobInfo(Parcel parcel) { @@ -195,20 +202,17 @@ public final class PrintJobInfo implements Parcelable { mCreationTime = parcel.readLong(); mCopies = parcel.readInt(); mStateReason = parcel.readString(); - if (parcel.readInt() == 1) { - Parcelable[] parcelables = parcel.readParcelableArray(null); + Parcelable[] parcelables = parcel.readParcelableArray(null); + if (parcelables != null) { mPageRanges = new PageRange[parcelables.length]; for (int i = 0; i < parcelables.length; i++) { mPageRanges[i] = (PageRange) parcelables[i]; } } - if (parcel.readInt() == 1) { - mAttributes = PrintAttributes.CREATOR.createFromParcel(parcel); - } - if (parcel.readInt() == 1) { - mDocumentInfo = PrintDocumentInfo.CREATOR.createFromParcel(parcel); - } + mAttributes = (PrintAttributes) parcel.readParcelable(null); + mDocumentInfo = (PrintDocumentInfo) parcel.readParcelable(null); mCanceling = (parcel.readInt() == 1); + mAdvancedOptions = parcel.readBundle(); } /** @@ -297,6 +301,14 @@ public final class PrintJobInfo implements Parcelable { * Gets the current job state. * * @return The job state. + * + * @see #STATE_CREATED + * @see #STATE_QUEUED + * @see #STATE_STARTED + * @see #STATE_COMPLETED + * @see #STATE_BLOCKED + * @see #STATE_FAILED + * @see #STATE_CANCELED */ public int getState() { return mState; @@ -511,6 +523,71 @@ public final class PrintJobInfo implements Parcelable { mCanceling = cancelling; } + /** + * Gets whether this job has a given advanced (printer specific) print + * option. + * + * @param key The option key. + * @return Whether the option is present. + * + * @hide + */ + public boolean hasAdvancedOption(String key) { + return mAdvancedOptions != null && mAdvancedOptions.containsKey(key); + } + + /** + * Gets the value of an advanced (printer specific) print option. + * + * @param key The option key. + * @return The option value. + * + * @hide + */ + public String getAdvancedStringOption(String key) { + if (mAdvancedOptions != null) { + return mAdvancedOptions.getString(key); + } + return null; + } + + /** + * Gets the value of an advanced (printer specific) print option. + * + * @param key The option key. + * @return The option value. + * + * @hide + */ + public int getAdvancedIntOption(String key) { + if (mAdvancedOptions != null) { + return mAdvancedOptions.getInt(key); + } + return 0; + } + + /** + * Gets the advanced options. + * + * @return The advanced options. + * + * @hide + */ + public Bundle getAdvancedOptions() { + return mAdvancedOptions; + } + + /** + * Sets the advanced options. + * + * @param options The advanced options. + * + * @hide + */ + public void setAdvancedOptions(Bundle options) { + mAdvancedOptions = options; + } + @Override public int describeContents() { return 0; @@ -528,25 +605,11 @@ public final class PrintJobInfo implements Parcelable { parcel.writeLong(mCreationTime); parcel.writeInt(mCopies); parcel.writeString(mStateReason); - if (mPageRanges != null) { - parcel.writeInt(1); - parcel.writeParcelableArray(mPageRanges, flags); - } else { - parcel.writeInt(0); - } - if (mAttributes != null) { - parcel.writeInt(1); - mAttributes.writeToParcel(parcel, flags); - } else { - parcel.writeInt(0); - } - if (mDocumentInfo != null) { - parcel.writeInt(1); - mDocumentInfo.writeToParcel(parcel, flags); - } else { - parcel.writeInt(0); - } + parcel.writeParcelableArray(mPageRanges, flags); + parcel.writeParcelable(mAttributes, flags); + parcel.writeParcelable(mDocumentInfo, 0); parcel.writeInt(mCanceling ? 1 : 0); + parcel.writeBundle(mAdvancedOptions); } @Override @@ -567,6 +630,7 @@ public final class PrintJobInfo implements Parcelable { builder.append(", cancelling: " + mCanceling); builder.append(", pages: " + (mPageRanges != null ? Arrays.toString(mPageRanges) : null)); + builder.append(", hasAdvancedOptions: " + (mAdvancedOptions != null)); builder.append("}"); return builder.toString(); } @@ -611,7 +675,7 @@ public final class PrintJobInfo implements Parcelable { * Constructor. * * @param prototype Prototype to use as a starting point. - * Can be null. + * Can be <code>null</code>. */ public Builder(PrintJobInfo prototype) { mPrototype = (prototype != null) @@ -653,7 +717,10 @@ public final class PrintJobInfo implements Parcelable { * @param value The option value. */ public void putAdvancedOption(String key, String value) { - + if (mPrototype.mAdvancedOptions == null) { + mPrototype.mAdvancedOptions = new Bundle(); + } + mPrototype.mAdvancedOptions.putString(key, value); } /** @@ -663,7 +730,10 @@ public final class PrintJobInfo implements Parcelable { * @param value The option value. */ public void putAdvancedOption(String key, int value) { - + if (mPrototype.mAdvancedOptions == null) { + mPrototype.mAdvancedOptions = new Bundle(); + } + mPrototype.mAdvancedOptions.putInt(key, value); } /** diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java index 955b4d8..bbfc307 100644 --- a/core/java/android/print/PrintManager.java +++ b/core/java/android/print/PrintManager.java @@ -55,6 +55,49 @@ import java.util.Map; * PrintManager printManager = * (PrintManager) context.getSystemService(Context.PRINT_SERVICE); * </pre> + * + * <h3>Print mechanics</h3> + * <p> + * The key idea behind printing on the platform is that the content to be printed + * should be laid out for the currently selected print options resulting in an + * optimized output and higher user satisfaction. To achieve this goal the platform + * declares a contract that the printing application has to follow which is defined + * by the {@link PrintDocumentAdapter} class. At a higher level the contract is that + * when the user selects some options from the print UI that may affect the way + * content is laid out, for example page size, the application receives a callback + * allowing it to layout the content to better fit these new constraints. After a + * layout pass the system may ask the application to render one or more pages one + * or more times. For example, an application may produce a single column list for + * smaller page sizes and a multi-column table for larger page sizes. + * </p> + * <h3>Print jobs</h3> + * <p> + * Print jobs are started by calling the {@link #print(String, PrintDocumentAdapter, + * PrintAttributes)} from an activity which results in bringing up the system print + * UI. Once the print UI is up, when the user changes a selected print option that + * affects the way content is laid out the system starts to interact with the + * application following the mechanics described the section above. + * </p> + * <p> + * Print jobs can be in {@link PrintJobInfo#STATE_CREATED created}, {@link + * PrintJobInfo#STATE_QUEUED queued}, {@link PrintJobInfo#STATE_STARTED started}, + * {@link PrintJobInfo#STATE_BLOCKED blocked}, {@link PrintJobInfo#STATE_COMPLETED + * completed}, {@link PrintJobInfo#STATE_FAILED failed}, and {@link + * PrintJobInfo#STATE_CANCELED canceled} state. Print jobs are stored in dedicated + * system spooler until they are handled which is they are cancelled or completed. + * Active print jobs, ones that are not cancelled or completed, are considered failed + * if the device reboots as the new boot may be after a very long time. The user may + * choose to restart such print jobs. Once a print job is queued all relevant content + * is stored in the system spooler and its lifecycle becomes detached from this of + * the application that created it. + * </p> + * <p> + * An applications can query the print spooler for current print jobs it created + * but not print jobs created by other applications. + * </p> + * + * @see PrintJob + * @see PrintJobInfo */ public final class PrintManager { @@ -292,20 +335,54 @@ public final class PrintManager { /** * Creates a print job for printing a {@link PrintDocumentAdapter} with * default print attributes. - * - * @param printJobName A name for the new print job. + * <p> + * Calling this method brings the print UI allowing the user to customize + * the print job and returns a {@link PrintJob} object without waiting for the + * user to customize or confirm the print job. The returned print job instance + * is in a {@link PrintJobInfo#STATE_CREATED created} state. + * <p> + * This method can be called only from an {@link Activity}. The rationale is that + * printing from a service will create an inconsistent user experience as the print + * UI would appear without any context. + * </p> + * <p> + * Also the passed in {@link PrintDocumentAdapter} will be considered invalid if + * your activity is finished. The rationale is that once the activity that + * initiated printing is finished, the provided adapter may be in an inconsistent + * state as it may depend on the UI presented by the activity. + * </p> + * <p> + * The default print attributes are a hint to the system how the data is to + * be printed. For example, a photo editor may look at the photo aspect ratio + * to determine the default orientation and provide a hint whether the printing + * should be in portrait or landscape. The system will do a best effort to + * selected the hinted options in the print dialog, given the current printer + * supports them. + * </p> + * + * @param printJobName A name for the new print job which is shown to the user. * @param documentAdapter An adapter that emits the document to print. - * @param attributes The default print job attributes. + * @param attributes The default print job attributes or <code>null</code>. * @return The created print job on success or null on failure. + * @throws IllegalStateException If not called from an {@link Activity}. + * @throws IllegalArgumentException If the print job name is empty or the + * document adapter is null. + * * @see PrintJob */ public PrintJob print(String printJobName, PrintDocumentAdapter documentAdapter, PrintAttributes attributes) { + if (!(mContext instanceof Activity)) { + throw new IllegalStateException("Can print only from an activity"); + } if (TextUtils.isEmpty(printJobName)) { - throw new IllegalArgumentException("priintJobName cannot be empty"); + throw new IllegalArgumentException("printJobName cannot be empty"); + } + if (documentAdapter == null) { + throw new IllegalArgumentException("documentAdapter cannot be null"); } PrintDocumentAdapterDelegate delegate = new PrintDocumentAdapterDelegate( - mContext, documentAdapter); + (Activity) mContext, documentAdapter); try { Bundle result = mService.print(printJobName, delegate, attributes, mContext.getPackageName(), mAppId, mUserId); @@ -398,12 +475,9 @@ public final class PrintManager { private boolean mDestroyed; - public PrintDocumentAdapterDelegate(Context context, + public PrintDocumentAdapterDelegate(Activity activity, PrintDocumentAdapter documentAdapter) { - if (!(context instanceof Activity)) { - throw new IllegalStateException("Can print only from an activity"); - } - mActivity = (Activity) context; + mActivity = activity; mDocumentAdapter = documentAdapter; mHandler = new MyHandler(mActivity.getMainLooper()); mActivity.getApplication().registerActivityLifecycleCallbacks(this); diff --git a/core/java/android/print/PrinterCapabilitiesInfo.java b/core/java/android/print/PrinterCapabilitiesInfo.java index df51ec1..b615600 100644 --- a/core/java/android/print/PrinterCapabilitiesInfo.java +++ b/core/java/android/print/PrinterCapabilitiesInfo.java @@ -24,10 +24,17 @@ import android.print.PrintAttributes.Resolution; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; /** - * This class represents the capabilities of a printer. + * This class represents the capabilities of a printer. Instances + * of this class are created by a print service to report the + * capabilities of a printer it manages. The capabilities of a + * printer specify how it can print content. For example, what + * are the media sizes supported by the printer, what are the + * minimal margins of the printer based on its technical design, + * etc. */ public final class PrinterCapabilitiesInfo implements Parcelable { /** @@ -112,7 +119,7 @@ public final class PrinterCapabilitiesInfo implements Parcelable { * @return The media sizes. */ public List<MediaSize> getMediaSizes() { - return mMediaSizes; + return Collections.unmodifiableList(mMediaSizes); } /** @@ -121,7 +128,7 @@ public final class PrinterCapabilitiesInfo implements Parcelable { * @return The resolutions. */ public List<Resolution> getResolutions() { - return mResolutions; + return Collections.unmodifiableList(mResolutions); } /** @@ -135,9 +142,9 @@ public final class PrinterCapabilitiesInfo implements Parcelable { } /** - * Gets the supported color modes. + * Gets the bit mask of supported color modes. * - * @return The color modes. + * @return The bit mask of supported color modes. * * @see PrintAttributes#COLOR_MODE_COLOR * @see PrintAttributes#COLOR_MODE_MONOCHROME @@ -355,9 +362,10 @@ public final class PrinterCapabilitiesInfo implements Parcelable { } /** - * Builder for creating of a {@link PrinterInfo}. This class is responsible - * to enforce that all required attributes have at least one default value. - * In other words, this class creates only well-formed {@link PrinterInfo}s. + * Builder for creating of a {@link PrinterCapabilitiesInfo}. This class is + * responsible to enforce that all required attributes have at least one + * default value. In other words, this class creates only well-formed {@link + * PrinterCapabilitiesInfo}s. * <p> * Look at the individual methods for a reference whether a property is * required or if it is optional. @@ -369,9 +377,9 @@ public final class PrinterCapabilitiesInfo implements Parcelable { /** * Creates a new instance. * - * @param printerId The printer id. Cannot be null. + * @param printerId The printer id. Cannot be <code>null</code>. * - * @throws IllegalArgumentException If the printer id is null. + * @throws IllegalArgumentException If the printer id is <code>null</code>. */ public Builder(PrinterId printerId) { if (printerId == null) { @@ -492,7 +500,7 @@ public final class PrinterCapabilitiesInfo implements Parcelable { /** * Crates a new {@link PrinterCapabilitiesInfo} enforcing that all - * required properties have need specified. See individual methods + * required properties have been specified. See individual methods * in this class for reference about required attributes. * * @return A new {@link PrinterCapabilitiesInfo}. @@ -521,7 +529,7 @@ public final class PrinterCapabilitiesInfo implements Parcelable { if (mPrototype.mMinMargins == null) { throw new IllegalArgumentException("margins cannot be null"); } - return new PrinterCapabilitiesInfo(mPrototype); + return mPrototype; } private void throwIfDefaultAlreadySpecified(int propertyIndex) { diff --git a/core/java/android/print/PrinterInfo.java b/core/java/android/print/PrinterInfo.java index ad79a38..7fcc81f 100644 --- a/core/java/android/print/PrinterInfo.java +++ b/core/java/android/print/PrinterInfo.java @@ -21,7 +21,12 @@ import android.os.Parcelable; import android.text.TextUtils; /** - * This class represents the description of a printer. + * This class represents the description of a printer. Instances of + * this class are created by print services to report to the system + * the printers they manage. The information of this class has two + * major components, printer properties such as name, id, status, + * description and printer capabilities which describe the various + * print modes a printer supports such as media sizes, margins, etc. */ public final class PrinterInfo implements Parcelable { @@ -96,6 +101,10 @@ public final class PrinterInfo implements Parcelable { * Gets the printer status. * * @return The status. + * + * @see #STATUS_BUSY + * @see #STATUS_IDLE + * @see #STATUS_UNAVAILABLE */ public int getStatus() { return mStatus; @@ -216,6 +225,8 @@ public final class PrinterInfo implements Parcelable { * @param printerId The printer id. Cannot be null. * @param name The printer name. Cannot be empty. * @param status The printer status. Must be a valid status. + * @throws IllegalArgumentException If the printer id is null, or the + * printer name is empty or the status is not a valid one. */ public Builder(PrinterId printerId, String name, int status) { if (printerId == null) { @@ -259,7 +270,8 @@ public final class PrinterInfo implements Parcelable { } /** - * Sets the printer name. + * Sets the <strong>localized</strong> printer name which + * is shown to the user * * @param name The name. * @return This builder. @@ -270,7 +282,8 @@ public final class PrinterInfo implements Parcelable { } /** - * Sets the printer description. + * Sets the <strong>localized</strong> printer description + * which is shown to the user * * @param description The description. * @return This builder. @@ -292,12 +305,12 @@ public final class PrinterInfo implements Parcelable { } /** - * Crates a new {@link PrinterInfo}. + * Creates a new {@link PrinterInfo}. * * @return A new {@link PrinterInfo}. */ public PrinterInfo build() { - return new PrinterInfo(mPrototype); + return mPrototype; } private boolean isValidStatus(int status) { diff --git a/core/java/android/print/package.html b/core/java/android/print/package.html new file mode 100644 index 0000000..579567d --- /dev/null +++ b/core/java/android/print/package.html @@ -0,0 +1,46 @@ +<HTML> +<BODY> +<h3>Overview</h3> +<p> +Provides classes for implementing print support in applications and also contains all +base classes and abstractions involved in printing. These base classes are also used +by other more specialized printing related packages. +</p> +<p> +The entry point for interacting with the print system is the {@link android.print.PrintManager} +which is a system service that can be obtained from the current context. The print manager +provides APIs for printing, querying the state of print jobs, etc. +<p/> +<h3>Print contract</h3> +<p> +An application that wants to implement printing must extend +{@link android.print.PrintDocumentAdapter} which defines the contract between the system +and the application.The key idea behind this adapter is that the printed content may change +based on the selected print options, such as media size, orientation, which +requires the content to be re-laid out. The constraints according to which the content has +to be laid out are encapsulated in the {@link android.print.PrintAttributes} class. Once +layout is completed the application calls back to the system passing a +{@link android.print.PrintDocumentInfo} instance which describes the generated content. After +the content has been laid out the application may be asked to render some pages of that content +for preview or printing. The range of pages that have to be rendered is abstracted by the +{@link android.print.PageRange} class. +</p> +<h3>Print jobs</h3> +<p> +A print job is represented by the {@link android.print.PrintJob} class which has behavior +methods as well as methods for querying its state. Each print job has a unique id represented +by the {@link android.print.PrintJobId} class and exposes APIs for obtaining a {@link +android.print.PrintJobInfo} which is a snapshot of its state. The print job state may +change over time. +</p> +<h3>Printers</h3> +<p> +An available printer represented by the {@link android.print.PrinterInfo} class has a +unique id which is abstracted by the {@link android.print.PrinterId} class. The {@link +android.print.PrinterInfo} contains printer properties such as id, name, description, status, +and printer capabilities encapsulated in the {@link android.print.PrinterCapabilitiesInfo} +class. Printer capabilities describe how a printer can print content, for example what are +the supported media sizes, color modes, resolutions, etc. +<p> +</BODY> +</HTML> diff --git a/core/java/android/printservice/PrintJob.java b/core/java/android/printservice/PrintJob.java index fdeb373..6fa0bdd 100644 --- a/core/java/android/printservice/PrintJob.java +++ b/core/java/android/printservice/PrintJob.java @@ -321,7 +321,7 @@ public final class PrintJob { */ public String getAdvancedStringOption(String key) { PrintService.throwIfNotCalledOnMainThread(); - return null; + return getInfo().getAdvancedStringOption(key); } /** @@ -333,7 +333,7 @@ public final class PrintJob { */ public boolean hasAdvancedOption(String key) { PrintService.throwIfNotCalledOnMainThread(); - return false; + return getInfo().hasAdvancedOption(key); } /** @@ -344,7 +344,7 @@ public final class PrintJob { */ public int getAdvancedIntOption(String key) { PrintService.throwIfNotCalledOnMainThread(); - return 0; + return getInfo().getAdvancedIntOption(key); } @Override diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java index 0fc5f7f..eb0ac2e 100644 --- a/core/java/android/printservice/PrintService.java +++ b/core/java/android/printservice/PrintService.java @@ -209,6 +209,14 @@ public abstract class PrintService extends Service { * PrintJob#getAdvancedStringOption(String) PrintJob.getAdvancedStringOption(String)} * and {@link PrintJob#getAdvancedIntOption(String) PrintJob.getAdvancedIntOption(String)}. * </p> + * <p> + * If the advanced print options activity offers changes to the standard print + * options, you can get the current {@link android.print.PrinterInfo} using the + * "android.intent.extra.print.EXTRA_PRINTER_INFO" extra which will allow you to + * present the user with UI options supported by the current printer. For example, + * if the current printer does not support a give media size, you should not + * offer it in the advanced print options dialog. + * </p> */ public static final String EXTRA_PRINT_JOB_INFO = "android.intent.extra.print.PRINT_JOB_INFO"; diff --git a/core/java/android/printservice/PrintServiceInfo.java b/core/java/android/printservice/PrintServiceInfo.java index 8e9636c..a2c6c09 100644 --- a/core/java/android/printservice/PrintServiceInfo.java +++ b/core/java/android/printservice/PrintServiceInfo.java @@ -60,6 +60,8 @@ public final class PrintServiceInfo implements Parcelable { private final String mAddPrintersActivityName; + private final String mAdvancedPrintOptionsActivityName; + /** * Creates a new instance. * @@ -70,6 +72,7 @@ public final class PrintServiceInfo implements Parcelable { mResolveInfo = parcel.readParcelable(null); mSettingsActivityName = parcel.readString(); mAddPrintersActivityName = parcel.readString(); + mAdvancedPrintOptionsActivityName = parcel.readString(); } /** @@ -78,14 +81,16 @@ public final class PrintServiceInfo implements Parcelable { * @param resolveInfo The service resolve info. * @param settingsActivityName Optional settings activity name. * @param addPrintersActivityName Optional add printers activity name. + * @param advancedPrintOptionsActivityName Optional advanced print options activity. */ public PrintServiceInfo(ResolveInfo resolveInfo, String settingsActivityName, - String addPrintersActivityName) { + String addPrintersActivityName, String advancedPrintOptionsActivityName) { mId = new ComponentName(resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name).flattenToString(); mResolveInfo = resolveInfo; mSettingsActivityName = settingsActivityName; mAddPrintersActivityName = addPrintersActivityName; + mAdvancedPrintOptionsActivityName = advancedPrintOptionsActivityName; } /** @@ -99,6 +104,7 @@ public final class PrintServiceInfo implements Parcelable { public static PrintServiceInfo create(ResolveInfo resolveInfo, Context context) { String settingsActivityName = null; String addPrintersActivityName = null; + String advancedPrintOptionsActivityName = null; XmlResourceParser parser = null; PackageManager packageManager = context.getPackageManager(); @@ -128,6 +134,9 @@ public final class PrintServiceInfo implements Parcelable { addPrintersActivityName = attributes.getString( com.android.internal.R.styleable.PrintService_addPrintersActivity); + advancedPrintOptionsActivityName = attributes.getString(com.android.internal + .R.styleable.PrintService_advancedPrintOptionsActivity); + attributes.recycle(); } } catch (IOException ioe) { @@ -144,7 +153,8 @@ public final class PrintServiceInfo implements Parcelable { } } - return new PrintServiceInfo(resolveInfo, settingsActivityName, addPrintersActivityName); + return new PrintServiceInfo(resolveInfo, settingsActivityName, + addPrintersActivityName, advancedPrintOptionsActivityName); } /** @@ -195,6 +205,19 @@ public final class PrintServiceInfo implements Parcelable { } /** + * The advanced print options activity name. + * <p> + * <strong>Statically set from + * {@link PrintService#SERVICE_META_DATA meta-data}.</strong> + * </p> + * + * @return The advanced print options activity name. + */ + public String getAdvancedOptionsActivityName() { + return mAdvancedPrintOptionsActivityName; + } + + /** * {@inheritDoc} */ public int describeContents() { @@ -206,6 +229,7 @@ public final class PrintServiceInfo implements Parcelable { parcel.writeParcelable(mResolveInfo, 0); parcel.writeString(mSettingsActivityName); parcel.writeString(mAddPrintersActivityName); + parcel.writeString(mAdvancedPrintOptionsActivityName); } @Override @@ -243,6 +267,8 @@ public final class PrintServiceInfo implements Parcelable { builder.append(", resolveInfo=").append(mResolveInfo); builder.append(", settingsActivityName=").append(mSettingsActivityName); builder.append(", addPrintersActivityName=").append(mAddPrintersActivityName); + builder.append(", advancedPrintOptionsActivityName=") + .append(mAdvancedPrintOptionsActivityName); builder.append("}"); return builder.toString(); } diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index 7f8dca2..09f5fe3 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -81,6 +81,9 @@ public final class DocumentsContract { /** {@hide} */ public static final String EXTRA_PACKAGE_NAME = "android.content.extra.PACKAGE_NAME"; + /** {@hide} */ + public static final String EXTRA_SHOW_ADVANCED = "android.content.extra.SHOW_ADVANCED"; + /** * Included in {@link AssetFileDescriptor#getExtras()} when returned * thumbnail should be rotated. diff --git a/core/java/android/transition/Scene.java b/core/java/android/transition/Scene.java index 5800bd5..e1f1896 100644 --- a/core/java/android/transition/Scene.java +++ b/core/java/android/transition/Scene.java @@ -57,7 +57,7 @@ public final class Scene { com.android.internal.R.id.scene_layoutid_cache); if (scenes == null) { scenes = new SparseArray<Scene>(); - sceneRoot.setTag(com.android.internal.R.id.scene_layoutid_cache, scenes); + sceneRoot.setTagInternal(com.android.internal.R.id.scene_layoutid_cache, scenes); } Scene scene = scenes.get(layoutId); if (scene != null) { diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java index 9b1494d..3bf6790 100644 --- a/core/java/android/transition/TransitionManager.java +++ b/core/java/android/transition/TransitionManager.java @@ -82,6 +82,8 @@ public class TransitionManager { * an {@link AutoTransition} instance. * * @param transition The default transition to be used for scene changes. + * + * @hide pending later changes */ public void setDefaultTransition(Transition transition) { sDefaultTransition = transition; @@ -93,6 +95,8 @@ public class TransitionManager { * * @return The current default transition. * @see #setDefaultTransition(Transition) + * + * @hide pending later changes */ public static Transition getDefaultTransition() { return sDefaultTransition; @@ -105,7 +109,7 @@ public class TransitionManager { * transition to run. * @param transition The transition that will play when the given scene is * entered. A value of null will result in the default behavior of - * using the {@link #getDefaultTransition() default transition} instead. + * using the default transition instead. */ public void setTransition(Scene scene, Transition transition) { mSceneTransitions.put(scene, transition); @@ -121,7 +125,7 @@ public class TransitionManager { * be run * @param transition The transition that will play when the given scene is * entered. A value of null will result in the default behavior of - * using the {@link #getDefaultTransition() default transition} instead. + * using the default transition instead. */ public void setTransition(Scene fromScene, Scene toScene, Transition transition) { ArrayMap<Scene, Transition> sceneTransitionMap = mScenePairTransitions.get(toScene); @@ -139,8 +143,8 @@ public class TransitionManager { * * @param scene The scene being entered * @return The Transition to be used for the given scene change. If no - * Transition was specified for this scene change, the {@link #getDefaultTransition() - * default transition} will be used instead. + * Transition was specified for this scene change, the default transition + * will be used instead. */ private Transition getTransition(Scene scene) { Transition transition = null; diff --git a/core/java/android/widget/ActivityChooserModel.java b/core/java/android/widget/ActivityChooserModel.java index 736566e..61df922 100644 --- a/core/java/android/widget/ActivityChooserModel.java +++ b/core/java/android/widget/ActivityChooserModel.java @@ -16,9 +16,12 @@ package android.widget; +import android.app.ActivityManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.database.DataSetObservable; import android.os.AsyncTask; @@ -708,7 +711,12 @@ public class ActivityChooserModel extends DataSetObservable { final int resolveInfoCount = resolveInfos.size(); for (int i = 0; i < resolveInfoCount; i++) { ResolveInfo resolveInfo = resolveInfos.get(i); - mActivities.add(new ActivityResolveInfo(resolveInfo)); + ActivityInfo activityInfo = resolveInfo.activityInfo; + if (ActivityManager.checkComponentPermission(activityInfo.permission, + android.os.Process.myUid(), activityInfo.applicationInfo.uid, + activityInfo.exported) == PackageManager.PERMISSION_GRANTED) { + mActivities.add(new ActivityResolveInfo(resolveInfo)); + } } return true; } @@ -930,29 +938,31 @@ public class ActivityChooserModel extends DataSetObservable { private final class DefaultSorter implements ActivitySorter { private static final float WEIGHT_DECAY_COEFFICIENT = 0.95f; - private final Map<String, ActivityResolveInfo> mPackageNameToActivityMap = - new HashMap<String, ActivityResolveInfo>(); + private final Map<ComponentName, ActivityResolveInfo> mPackageNameToActivityMap = + new HashMap<ComponentName, ActivityResolveInfo>(); public void sort(Intent intent, List<ActivityResolveInfo> activities, List<HistoricalRecord> historicalRecords) { - Map<String, ActivityResolveInfo> packageNameToActivityMap = - mPackageNameToActivityMap; - packageNameToActivityMap.clear(); + Map<ComponentName, ActivityResolveInfo> componentNameToActivityMap = + mPackageNameToActivityMap; + componentNameToActivityMap.clear(); final int activityCount = activities.size(); for (int i = 0; i < activityCount; i++) { ActivityResolveInfo activity = activities.get(i); activity.weight = 0.0f; - String packageName = activity.resolveInfo.activityInfo.packageName; - packageNameToActivityMap.put(packageName, activity); + ComponentName componentName = new ComponentName( + activity.resolveInfo.activityInfo.packageName, + activity.resolveInfo.activityInfo.name); + componentNameToActivityMap.put(componentName, activity); } final int lastShareIndex = historicalRecords.size() - 1; float nextRecordWeight = 1; for (int i = lastShareIndex; i >= 0; i--) { HistoricalRecord historicalRecord = historicalRecords.get(i); - String packageName = historicalRecord.activity.getPackageName(); - ActivityResolveInfo activity = packageNameToActivityMap.get(packageName); + ComponentName componentName = historicalRecord.activity; + ActivityResolveInfo activity = componentNameToActivityMap.get(componentName); if (activity != null) { activity.weight += historicalRecord.weight * nextRecordWeight; nextRecordWeight = nextRecordWeight * WEIGHT_DECAY_COEFFICIENT; diff --git a/core/java/android/widget/ActivityChooserView.java b/core/java/android/widget/ActivityChooserView.java index dff1531..8612964 100644 --- a/core/java/android/widget/ActivityChooserView.java +++ b/core/java/android/widget/ActivityChooserView.java @@ -18,6 +18,7 @@ package android.widget; import com.android.internal.R; +import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; @@ -27,6 +28,7 @@ import android.content.res.TypedArray; import android.database.DataSetObserver; import android.graphics.drawable.Drawable; import android.util.AttributeSet; +import android.util.Log; import android.view.ActionProvider; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -63,6 +65,8 @@ import android.widget.ListPopupWindow.ForwardingListener; */ public class ActivityChooserView extends ViewGroup implements ActivityChooserModelClient { + private static final String LOG_TAG = "ActivityChooserView"; + /** * An adapter for displaying the activities in an {@link AdapterView}. */ @@ -543,9 +547,9 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod } // Activity chooser content. if (mDefaultActivityButton.getVisibility() == VISIBLE) { - mActivityChooserContent.setBackgroundDrawable(mActivityChooserContentBackground); + mActivityChooserContent.setBackground(mActivityChooserContentBackground); } else { - mActivityChooserContent.setBackgroundDrawable(null); + mActivityChooserContent.setBackground(null); } } @@ -577,7 +581,8 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod Intent launchIntent = mAdapter.getDataModel().chooseActivity(position); if (launchIntent != null) { launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); - mContext.startActivity(launchIntent); + ResolveInfo resolveInfo = mAdapter.getDataModel().getActivity(position); + startActivity(launchIntent, resolveInfo); } } } break; @@ -595,7 +600,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod Intent launchIntent = mAdapter.getDataModel().chooseActivity(index); if (launchIntent != null) { launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); - mContext.startActivity(launchIntent); + startActivity(launchIntent, defaultActivity); } } else if (view == mExpandActivityOverflowButton) { mIsSelectingDefaultActivity = false; @@ -632,6 +637,18 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod mOnDismissListener.onDismiss(); } } + + private void startActivity(Intent intent, ResolveInfo resolveInfo) { + try { + mContext.startActivity(intent); + } catch (RuntimeException re) { + CharSequence appLabel = resolveInfo.loadLabel(mContext.getPackageManager()); + String message = mContext.getString( + R.string.activitychooserview_choose_application_error, appLabel); + Log.e(LOG_TAG, message); + Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show(); + } + } } /** @@ -805,10 +822,6 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod return mDataModel.getHistorySize(); } - public int getMaxActivityCount() { - return mMaxActivityCount; - } - public ActivityChooserModel getDataModel() { return mDataModel; } |
