diff options
Diffstat (limited to 'core')
| -rw-r--r-- | core/java/android/app/ActivityManager.java | 41 | ||||
| -rw-r--r-- | core/java/android/app/ActivityManagerNative.java | 9 | ||||
| -rw-r--r-- | core/java/android/app/AppOpsManager.java | 22 | ||||
| -rw-r--r-- | core/java/android/app/IActivityManager.java | 3 | ||||
| -rw-r--r-- | core/java/android/app/VoiceInteractor.java | 30 | ||||
| -rw-r--r-- | core/java/android/content/res/Resources.java | 96 | ||||
| -rw-r--r-- | core/java/android/hardware/camera2/CameraCharacteristics.java | 4 | ||||
| -rw-r--r-- | core/java/android/hardware/camera2/CameraMetadata.java | 20 | ||||
| -rw-r--r-- | core/java/android/service/voice/VoiceInteractionSession.java | 10 | ||||
| -rw-r--r-- | core/java/android/text/Hyphenator.java | 33 | ||||
| -rw-r--r-- | core/java/android/widget/RadialTimePickerView.java | 187 | ||||
| -rw-r--r-- | core/java/com/android/internal/app/DumpHeapActivity.java | 37 | ||||
| -rw-r--r-- | core/java/com/android/internal/os/ZygoteInit.java | 6 | ||||
| -rw-r--r-- | core/jni/android/graphics/Paint.cpp | 3 |
14 files changed, 333 insertions, 168 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index d143f8b..8f125d7 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -2682,6 +2682,47 @@ public class ActivityManager { } /** + * Request that the system start watching for the calling process to exceed a pss + * size as given here. Once called, the system will look for any occassions where it + * sees the associated process with a larger pss size and, when this happens, automatically + * pull a heap dump from it and allow the user to share the data. Note that this request + * continues running even if the process is killed and restarted. To remove the watch, + * use {@link #clearWatchHeapLimit()}. + * + * <p>This API only work if running on a debuggable (userdebug or eng) build.</p> + * + * <p>Callers can optionally implement {@link #ACTION_REPORT_HEAP_LIMIT} to directly + * handle heap limit reports themselves.</p> + * + * @param pssSize The size in bytes to set the limit at. + */ + public void setWatchHeapLimit(long pssSize) { + try { + ActivityManagerNative.getDefault().setDumpHeapDebugLimit(null, 0, pssSize, + mContext.getPackageName()); + } catch (RemoteException e) { + } + } + + /** + * Action an app can implement to handle reports from {@link #setWatchHeapLimit(long)}. + * If your package has an activity handling this action, it will be launched with the + * heap data provided to it the same way as {@link Intent#ACTION_SEND}. Note that to + * match the activty must support this action and a MIME type of "*/*". + */ + public static final String ACTION_REPORT_HEAP_LIMIT = "android.app.action.REPORT_HEAP_LIMIT"; + + /** + * Clear a heap watch limit previously set by {@link #setWatchHeapLimit(long)}. + */ + public void clearWatchHeapLimit() { + try { + ActivityManagerNative.getDefault().setDumpHeapDebugLimit(null, 0, 0, null); + } catch (RemoteException e) { + } + } + + /** * @hide */ public void startLockTaskMode(int taskId) { diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 1484af8..be7287f 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -2427,8 +2427,10 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM case SET_DUMP_HEAP_DEBUG_LIMIT_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); String procName = data.readString(); + int uid = data.readInt(); long maxMemSize = data.readLong(); - setDumpHeapDebugLimit(procName, maxMemSize); + String reportPackage = data.readString(); + setDumpHeapDebugLimit(procName, uid, maxMemSize, reportPackage); reply.writeNoException(); return true; } @@ -5644,12 +5646,15 @@ class ActivityManagerProxy implements IActivityManager } @Override - public void setDumpHeapDebugLimit(String processName, long maxMemSize) throws RemoteException { + public void setDumpHeapDebugLimit(String processName, int uid, long maxMemSize, + String reportPackage) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeString(processName); + data.writeInt(uid); data.writeLong(maxMemSize); + data.writeString(reportPackage); mRemote.transact(SET_DUMP_HEAP_DEBUG_LIMIT_TRANSACTION, data, reply, 0); reply.readException(); data.recycle(); diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 4bd2332..381c20c 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -208,8 +208,12 @@ public class AppOpsManager { public static final int OP_ACTIVATE_VPN = 47; /** @hide Access the WallpaperManagerAPI to write wallpapers. */ public static final int OP_WRITE_WALLPAPER = 48; + /** @hide Received the assist structure from an app. */ + public static final int OP_ASSIST_STRUCTURE = 49; + /** @hide Received a screenshot from assist. */ + public static final int OP_ASSIST_SCREENSHOT = 50; /** @hide */ - public static final int _NUM_OP = 49; + public static final int _NUM_OP = 51; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = @@ -288,6 +292,8 @@ public class AppOpsManager { OP_PROJECT_MEDIA, OP_ACTIVATE_VPN, OP_WRITE_WALLPAPER, + OP_ASSIST_STRUCTURE, + OP_ASSIST_SCREENSHOT, }; /** @@ -344,6 +350,8 @@ public class AppOpsManager { null, OPSTR_ACTIVATE_VPN, null, + null, + null, }; /** @@ -400,6 +408,8 @@ public class AppOpsManager { "PROJECT_MEDIA", "ACTIVATE_VPN", "WRITE_WALLPAPER", + "ASSIST_STRUCTURE", + "ASSIST_SCREENSHOT" }; /** @@ -456,6 +466,8 @@ public class AppOpsManager { null, // no permission for projecting media null, // no permission for activating vpn null, // no permission for supporting wallpaper + null, // no permission for receiving assist structure + null, // no permission for receiving assist screenshot }; /** @@ -513,6 +525,8 @@ public class AppOpsManager { null, //PROJECT_MEDIA UserManager.DISALLOW_CONFIG_VPN, // ACTIVATE_VPN UserManager.DISALLOW_WALLPAPER, // WRITE_WALLPAPER + null, // ASSIST_STRUCTURE + null, // ASSIST_SCREENSHOT }; /** @@ -569,6 +583,8 @@ public class AppOpsManager { false, //PROJECT_MEDIA false, //ACTIVATE_VPN false, //WALLPAPER + false, //ASSIST_STRUCTURE + false, //ASSIST_SCREENSHOT }; /** @@ -624,6 +640,8 @@ public class AppOpsManager { AppOpsManager.MODE_IGNORED, // OP_PROJECT_MEDIA AppOpsManager.MODE_IGNORED, // OP_ACTIVATE_VPN AppOpsManager.MODE_ALLOWED, + AppOpsManager.MODE_ALLOWED, + AppOpsManager.MODE_ALLOWED, }; /** @@ -683,6 +701,8 @@ public class AppOpsManager { false, false, false, + false, + false, }; private static HashMap<String, Integer> sOpStrToOp = new HashMap<String, Integer>(); diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index d794aa3..e20b0da 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -482,7 +482,8 @@ public interface IActivityManager extends IInterface { public void systemBackupRestored() throws RemoteException; public void notifyCleartextNetwork(int uid, byte[] firstPacket) throws RemoteException; - public void setDumpHeapDebugLimit(String processName, long maxMemSize) throws RemoteException; + public void setDumpHeapDebugLimit(String processName, int uid, long maxMemSize, + String reportPackage) throws RemoteException; public void dumpHeapFinished(String path) throws RemoteException; public void setVoiceKeepAwake(IVoiceInteractionSession session, boolean keepAwake) diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java index da7bb05..7acf5f0 100644 --- a/core/java/android/app/VoiceInteractor.java +++ b/core/java/android/app/VoiceInteractor.java @@ -103,9 +103,9 @@ public class VoiceInteractor { request = pullRequest((IVoiceInteractorRequest)args.arg1, true); if (DEBUG) Log.d(TAG, "onCompleteVoice: req=" + ((IVoiceInteractorRequest)args.arg1).asBinder() + "/" + request - + " result=" + args.arg1); + + " result=" + args.arg2); if (request != null) { - ((CompleteVoiceRequest)request).onCompleteResult((Bundle) args.arg1); + ((CompleteVoiceRequest)request).onCompleteResult((Bundle) args.arg2); request.clear(); } break; @@ -297,6 +297,7 @@ public class VoiceInteractor { */ public static final class Option implements Parcelable { final CharSequence mLabel; + final int mIndex; ArrayList<CharSequence> mSynonyms; Bundle mExtras; @@ -308,6 +309,21 @@ public class VoiceInteractor { */ public Option(CharSequence label) { mLabel = label; + mIndex = -1; + } + + /** + * Creates an option that a user can select with their voice by matching the label + * or one of several synonyms. + * @param label The label that will both be matched against what the user speaks + * and displayed visually. + * @param index The location of this option within the overall set of options. + * Can be used to help identify which the option when it is returned from the + * voice interactor. + */ + public Option(CharSequence label, int index) { + mLabel = label; + mIndex = index; } /** @@ -328,6 +344,14 @@ public class VoiceInteractor { return mLabel; } + /** + * Return the index that was supplied in the constructor. + * If the option was constructed without an index, -1 is returned. + */ + public int getIndex() { + return mIndex; + } + public int countSynonyms() { return mSynonyms != null ? mSynonyms.size() : 0; } @@ -356,6 +380,7 @@ public class VoiceInteractor { Option(Parcel in) { mLabel = in.readCharSequence(); + mIndex = in.readInt(); mSynonyms = in.readCharSequenceList(); mExtras = in.readBundle(); } @@ -368,6 +393,7 @@ public class VoiceInteractor { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeCharSequence(mLabel); + dest.writeInt(mIndex); dest.writeCharSequenceList(mSynonyms); dest.writeBundle(mExtras); } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 299eb7e..334d180 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -117,6 +117,9 @@ public class Resources { private static final LongSparseArray<android.content.res.ConstantState<ColorStateList>> sPreloadedColorStateLists = new LongSparseArray<>(); + private static final String CACHE_NOT_THEMED = ""; + private static final String CACHE_NULL_THEME = "null_theme"; + // Pool of TypedArrays targeted to this Resources object. final SynchronizedPool<TypedArray> mTypedArrayPool = new SynchronizedPool<>(5); @@ -2441,8 +2444,8 @@ public class Resources { } } - // Next, check preloaded drawables. These are unthemed but may have - // themeable attributes. + // Next, check preloaded drawables. These may contain unresolved theme + // attributes. final ConstantState cs; if (isColorDrawable) { cs = sPreloadedColorDrawables.get(key); @@ -2450,42 +2453,49 @@ public class Resources { cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key); } - final Drawable dr; + Drawable dr; if (cs != null) { - final Drawable clonedDr = cs.newDrawable(this); - if (theme != null) { - dr = clonedDr.mutate(); - dr.applyTheme(theme); - dr.clearMutated(); - } else { - dr = clonedDr; - } + dr = cs.newDrawable(this); } else if (isColorDrawable) { dr = new ColorDrawable(value.data); } else { - dr = loadDrawableForCookie(value, id, theme); + dr = loadDrawableForCookie(value, id, null); + } + + // Determine if the drawable has unresolved theme attributes. If it + // does, we'll need to apply a theme and store it in a theme-specific + // cache. + final String cacheKey; + if (!dr.canApplyTheme()) { + cacheKey = CACHE_NOT_THEMED; + } else if (theme == null) { + cacheKey = CACHE_NULL_THEME; + } else { + cacheKey = theme.getKey(); + dr = dr.mutate(); + dr.applyTheme(theme); + dr.clearMutated(); } // If we were able to obtain a drawable, store it in the appropriate - // cache (either preload or themed). + // cache: preload, not themed, null theme, or theme-specific. if (dr != null) { dr.setChangingConfigurations(value.changingConfigurations); - cacheDrawable(value, theme, isColorDrawable, caches, key, dr); + cacheDrawable(value, isColorDrawable, caches, cacheKey, key, dr); } return dr; } - private void cacheDrawable(TypedValue value, Theme theme, boolean isColorDrawable, + private void cacheDrawable(TypedValue value, boolean isColorDrawable, ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches, - long key, Drawable dr) { + String cacheKey, long key, Drawable dr) { final ConstantState cs = dr.getConstantState(); if (cs == null) { return; } if (mPreloading) { - // Preloaded drawables never have a theme, but may be themeable. final int changingConfigs = cs.getChangingConfigurations(); if (isColorDrawable) { if (verifyPreloadConfig(changingConfigs, 0, value.resourceId, "drawable")) { @@ -2507,14 +2517,13 @@ public class Resources { } } else { synchronized (mAccessLock) { - final String themeKey = theme == null ? "" : theme.mKey; - LongSparseArray<WeakReference<ConstantState>> themedCache = caches.get(themeKey); + LongSparseArray<WeakReference<ConstantState>> themedCache = caches.get(cacheKey); if (themedCache == null) { // Clean out the caches before we add more. This shouldn't // happen very often. pruneCaches(caches); themedCache = new LongSparseArray<>(1); - caches.put(themeKey, themedCache); + caches.put(cacheKey, themedCache); } themedCache.put(key, new WeakReference<>(cs)); } @@ -2612,46 +2621,47 @@ public class Resources { ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches, long key, Theme theme) { synchronized (mAccessLock) { - final String themeKey = theme != null ? theme.mKey : ""; - final LongSparseArray<WeakReference<ConstantState>> themedCache = caches.get(themeKey); - if (themedCache != null) { - final Drawable themedDrawable = getCachedDrawableLocked(themedCache, key); - if (themedDrawable != null) { - return themedDrawable; - } + // First search theme-agnostic cache. + final Drawable unthemedDrawable = getCachedDrawableLocked( + caches, key, CACHE_NOT_THEMED); + if (unthemedDrawable != null) { + return unthemedDrawable; } - // No cached drawable, we'll need to create a new one. - return null; + // Next search theme-specific cache. + final String themeKey = theme != null ? theme.getKey() : CACHE_NULL_THEME; + return getCachedDrawableLocked(caches, key, themeKey); } } + private Drawable getCachedDrawableLocked( + ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches, + long key, String themeKey) { + final LongSparseArray<WeakReference<ConstantState>> cache = caches.get(themeKey); + if (cache != null) { + final ConstantState entry = getConstantStateLocked(cache, key); + if (entry != null) { + return entry.newDrawable(this); + } + } + return null; + } + private ConstantState getConstantStateLocked( LongSparseArray<WeakReference<ConstantState>> drawableCache, long key) { final WeakReference<ConstantState> wr = drawableCache.get(key); - if (wr != null) { // we have the key + if (wr != null) { final ConstantState entry = wr.get(); if (entry != null) { - //Log.i(TAG, "Returning cached drawable @ #" + - // Integer.toHexString(((Integer)key).intValue()) - // + " in " + this + ": " + entry); return entry; - } else { // our entry has been purged + } else { + // Our entry has been purged. drawableCache.delete(key); } } return null; } - private Drawable getCachedDrawableLocked( - LongSparseArray<WeakReference<ConstantState>> drawableCache, long key) { - final ConstantState entry = getConstantStateLocked(drawableCache, key); - if (entry != null) { - return entry.newDrawable(this); - } - return null; - } - @Nullable ColorStateList loadColorStateList(TypedValue value, int id, Theme theme) throws NotFoundException { diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 1503bf5..f28c96e 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -2354,8 +2354,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p>Camera devices that support the MANUAL_POST_PROCESSING capability will always contain * at least one of below mode combinations:</p> * <ul> - * <li>CONTRAST_CURVE and FAST</li> - * <li>GAMMA_VALUE, PRESET_CURVE, and FAST</li> + * <li>CONTRAST_CURVE, FAST and HIGH_QUALITY</li> + * <li>GAMMA_VALUE, PRESET_CURVE, FAST and HIGH_QUALITY</li> * </ul> * <p>This includes all FULL level devices.</p> * <p><b>Range of valid values:</b><br> diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 7f901c8..85c574a 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -852,8 +852,8 @@ public abstract class CameraMetadata<TKey> { /** * <p>Color correction processing operates at improved - * quality but reduced capture rate (relative to sensor raw - * output).</p> + * quality but the capture rate might be reduced (relative to sensor + * raw output rate)</p> * <p>Advanced white balance adjustments above and beyond * the specified white balance pipeline may be applied.</p> * <p>If AWB is enabled with <code>{@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode} != OFF</code>, then @@ -883,8 +883,8 @@ public abstract class CameraMetadata<TKey> { public static final int COLOR_CORRECTION_ABERRATION_MODE_FAST = 1; /** - * <p>Aberration correction operates at improved quality but reduced - * capture rate (relative to sensor raw output).</p> + * <p>Aberration correction operates at improved quality but the capture rate might be + * reduced (relative to sensor raw output rate)</p> * @see CaptureRequest#COLOR_CORRECTION_ABERRATION_MODE */ public static final int COLOR_CORRECTION_ABERRATION_MODE_HIGH_QUALITY = 2; @@ -1797,7 +1797,7 @@ public abstract class CameraMetadata<TKey> { public static final int EDGE_MODE_FAST = 1; /** - * <p>Apply high-quality edge enhancement, at a cost of reducing output frame rate.</p> + * <p>Apply high-quality edge enhancement, at a cost of possibly reduced output frame rate.</p> * @see CaptureRequest#EDGE_MODE */ public static final int EDGE_MODE_HIGH_QUALITY = 2; @@ -1852,7 +1852,7 @@ public abstract class CameraMetadata<TKey> { /** * <p>High-quality hot pixel correction is applied, at a cost - * of reducing frame rate relative to sensor raw output.</p> + * of possibly reduced frame rate relative to sensor raw output.</p> * <p>The hotpixel map may be returned in {@link CaptureResult#STATISTICS_HOT_PIXEL_MAP android.statistics.hotPixelMap}.</p> * * @see CaptureResult#STATISTICS_HOT_PIXEL_MAP @@ -1894,8 +1894,8 @@ public abstract class CameraMetadata<TKey> { public static final int NOISE_REDUCTION_MODE_FAST = 1; /** - * <p>High-quality noise reduction is applied, at the cost of reducing frame rate - * relative to sensor output.</p> + * <p>High-quality noise reduction is applied, at the cost of possibly reduced frame + * rate relative to sensor output.</p> * @see CaptureRequest#NOISE_REDUCTION_MODE */ public static final int NOISE_REDUCTION_MODE_HIGH_QUALITY = 2; @@ -2032,7 +2032,7 @@ public abstract class CameraMetadata<TKey> { /** * <p>Apply high-quality lens shading correction, at the - * cost of reduced frame rate.</p> + * cost of possibly reduced frame rate.</p> * @see CaptureRequest#SHADING_MODE */ public static final int SHADING_MODE_HIGH_QUALITY = 2; @@ -2105,7 +2105,7 @@ public abstract class CameraMetadata<TKey> { /** * <p>High-quality gamma mapping and color enhancement will be applied, at - * the cost of reduced frame rate compared to raw sensor output.</p> + * the cost of possibly reduced frame rate compared to raw sensor output.</p> * @see CaptureRequest#TONEMAP_MODE */ public static final int TONEMAP_MODE_HIGH_QUALITY = 2; diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java index 4c31f80..20d7079 100644 --- a/core/java/android/service/voice/VoiceInteractionSession.java +++ b/core/java/android/service/voice/VoiceInteractionSession.java @@ -520,6 +520,10 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { mCallbacks, true); } + public Context getContext() { + return mContext; + } + Request newRequest(IVoiceInteractorCallback callback) { synchronized (this) { Request req = new Request(callback, this); @@ -832,6 +836,12 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { return false; } + /** + * Called when the user presses the back button while focus is in the session UI. Note + * that this will only happen if the session UI has requested input focus in its window; + * otherwise, the back key will go to whatever window has focus and do whatever behavior + * it normally has there. + */ public void onBackPressed() { hide(); } diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java index f4dff9b..a99bdf5 100644 --- a/core/java/android/text/Hyphenator.java +++ b/core/java/android/text/Hyphenator.java @@ -30,7 +30,7 @@ import java.util.Locale; * * @hide */ -/* package */ class Hyphenator { +public class Hyphenator { // This class has deliberately simple lifetime management (no finalizer) because in // the common case a process will use a very small number of locales. @@ -45,20 +45,14 @@ import java.util.Locale; } public static long get(Locale locale) { - synchronized (sMap) { - Hyphenator result = sMap.get(locale); - if (result == null) { - result = loadHyphenator(locale); - sMap.put(locale, result); - } - return result == null ? 0 : result.mNativePtr; - } + Hyphenator result = sMap.get(locale); + return result == null ? 0 : result.mNativePtr; } private static Hyphenator loadHyphenator(Locale locale) { // TODO: find pattern dictionary (from system location) that best matches locale if (Locale.US.equals(locale)) { - File f = new File("/data/local/tmp/hyph-en-us.pat.txt"); + File f = new File(getSystemHyphenatorLocation(), "hyph-en-us.pat.txt"); try { RandomAccessFile rf = new RandomAccessFile(f, "r"); byte[] buf = new byte[(int)rf.length()]; @@ -68,9 +62,26 @@ import java.util.Locale; long nativePtr = StaticLayout.nLoadHyphenator(patternData); return new Hyphenator(nativePtr); } catch (IOException e) { - Log.e(TAG, "error loading hyphenation " + f); + Log.e(TAG, "error loading hyphenation " + f, e); } } return null; } + + private static File getSystemHyphenatorLocation() { + // TODO: move to a sensible location under system + return new File("/system/usr/hyphen-data"); + } + + /** + * Load hyphenation patterns at initialization time. We want to have patterns + * for all locales loaded and ready to use so we don't have to do any file IO + * on the UI thread when drawing text in different locales. + * + * @hide + */ + public static void init() { + Locale l = Locale.US; + sMap.put(l, loadHyphenator(l)); + } } diff --git a/core/java/android/widget/RadialTimePickerView.java b/core/java/android/widget/RadialTimePickerView.java index 143dea4..52e1728 100644 --- a/core/java/android/widget/RadialTimePickerView.java +++ b/core/java/android/widget/RadialTimePickerView.java @@ -79,8 +79,10 @@ public class RadialTimePickerView extends View { // Transparent alpha level private static final int ALPHA_TRANSPARENT = 0; - private static final int DEGREES_FOR_ONE_HOUR = 30; - private static final int DEGREES_FOR_ONE_MINUTE = 6; + private static final int HOURS_IN_DAY = 24; + private static final int MINUTES_IN_HOUR = 60; + private static final int DEGREES_FOR_ONE_HOUR = 360 / HOURS_IN_DAY; + private static final int DEGREES_FOR_ONE_MINUTE = 360 / MINUTES_IN_HOUR; private static final int[] HOURS_NUMBERS = {12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; private static final int[] HOURS_NUMBERS_24 = {0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; @@ -140,8 +142,7 @@ public class RadialTimePickerView extends View { private final float[] mInnerTextX = new float[12]; private final float[] mInnerTextY = new float[12]; - private final int[] mLineLength = new int[3]; - private final int[] mSelectionDegrees = new int[3]; + private final int[] mSelectionDegrees = new int[2]; private final ArrayList<Animator> mHoursToMinutesAnims = new ArrayList<>(); private final ArrayList<Animator> mMinuteToHoursAnims = new ArrayList<>(); @@ -168,13 +169,13 @@ public class RadialTimePickerView extends View { private int mYCenter; private int mCircleRadius; - private int mMinHypotenuseForInnerNumber; - private int mMaxHypotenuseForOuterNumber; - private int mHalfwayHypotenusePoint; + private int mMinDistForInnerNumber; + private int mMaxDistForOuterNumber; + private int mHalfwayDist; private String[] mOuterTextHours; private String[] mInnerTextHours; - private String[] mOuterTextMinutes; + private String[] mMinutesText; private AnimatorSet mTransition; private int mAmOrPm; @@ -462,11 +463,10 @@ public class RadialTimePickerView extends View { private void setCurrentHourInternal(int hour, boolean callback, boolean autoAdvance) { final int degrees = (hour % 12) * DEGREES_FOR_ONE_HOUR; mSelectionDegrees[HOURS] = degrees; - mSelectionDegrees[HOURS_INNER] = degrees; // 0 is 12 AM (midnight) and 12 is 12 PM (noon). final int amOrPm = (hour == 0 || (hour % 24) < 12) ? AM : PM; - final boolean isOnInnerCircle = mIs24HourMode && hour >= 1 && hour <= 12; + final boolean isOnInnerCircle = getInnerCircleForHour(hour); if (mAmOrPm != amOrPm || mIsOnInnerCircle != isOnInnerCircle) { mAmOrPm = amOrPm; mIsOnInnerCircle = isOnInnerCircle; @@ -488,8 +488,7 @@ public class RadialTimePickerView extends View { * @return the current hour between 0 and 23 (inclusive) */ public int getCurrentHour() { - return getHourForDegrees( - mSelectionDegrees[mIsOnInnerCircle ? HOURS_INNER : HOURS], mIsOnInnerCircle); + return getHourForDegrees(mSelectionDegrees[HOURS], mIsOnInnerCircle); } private int getHourForDegrees(int degrees, boolean innerCircle) { @@ -497,11 +496,11 @@ public class RadialTimePickerView extends View { if (mIs24HourMode) { // Convert the 12-hour value into 24-hour time based on where the // selector is positioned. - if (innerCircle && hour == 0) { - // Inner circle is 1 through 12. + if (!innerCircle && hour == 0) { + // Outer circle is 1 through 12. hour = 12; - } else if (!innerCircle && hour != 0) { - // Outer circle is 13 through 23 and 0. + } else if (innerCircle && hour != 0) { + // Inner circle is 13 through 23 and 0. hour += 12; } } else if (mAmOrPm == PM) { @@ -510,6 +509,9 @@ public class RadialTimePickerView extends View { return hour; } + /** + * @param hour the hour in 24-hour time or 12-hour time + */ private int getDegreesForHour(int hour) { // Convert to be 0-11. if (mIs24HourMode) { @@ -522,12 +524,19 @@ public class RadialTimePickerView extends View { return hour * DEGREES_FOR_ONE_HOUR; } + /** + * @param hour the hour in 24-hour time or 12-hour time + */ + private boolean getInnerCircleForHour(int hour) { + return mIs24HourMode && (hour == 0 || hour > 12); + } + public void setCurrentMinute(int minute) { setCurrentMinuteInternal(minute, true); } private void setCurrentMinuteInternal(int minute, boolean callback) { - mSelectionDegrees[MINUTES] = (minute % 60) * DEGREES_FOR_ONE_MINUTE; + mSelectionDegrees[MINUTES] = (minute % MINUTES_IN_HOUR) * DEGREES_FOR_ONE_MINUTE; invalidate(); @@ -572,6 +581,7 @@ public class RadialTimePickerView extends View { initData(); invalidate(); + mTouchHelper.invalidateRoot(); } public void showMinutes(boolean animate) { @@ -587,6 +597,7 @@ public class RadialTimePickerView extends View { initData(); invalidate(); + mTouchHelper.invalidateRoot(); } private void initHoursAndMinutesText() { @@ -608,7 +619,7 @@ public class RadialTimePickerView extends View { mInnerTextHours = mHours12Texts; } - mOuterTextMinutes = mMinutesTexts; + mMinutesText = mMinutesTexts; final int hoursAlpha = mShowHours ? ALPHA_OPAQUE : ALPHA_TRANSPARENT; mAlpha[HOURS].setValue(hoursAlpha); @@ -627,9 +638,9 @@ public class RadialTimePickerView extends View { mYCenter = getHeight() / 2; mCircleRadius = Math.min(mXCenter, mYCenter); - mMinHypotenuseForInnerNumber = mCircleRadius - mTextInset[HOURS_INNER] - mSelectorRadius; - mMaxHypotenuseForOuterNumber = mCircleRadius - mTextInset[HOURS] + mSelectorRadius; - mHalfwayHypotenusePoint = mCircleRadius - (mTextInset[HOURS] + mTextInset[HOURS_INNER]) / 2; + mMinDistForInnerNumber = mCircleRadius - mTextInset[HOURS_INNER] - mSelectorRadius; + mMaxDistForOuterNumber = mCircleRadius - mTextInset[HOURS] + mSelectorRadius; + mHalfwayDist = mCircleRadius - (mTextInset[HOURS] + mTextInset[HOURS_INNER]) / 2; calculatePositionsHours(); calculatePositionsMinutes(); @@ -674,6 +685,7 @@ public class RadialTimePickerView extends View { private void drawMinutes(Canvas canvas, float alphaMod) { final int minutesAlpha = (int) (mAlpha[MINUTES].getValue() * alphaMod + 0.5f); if (minutesAlpha > 0) { + // Draw the minute selector under the elements. drawSelector(canvas, MINUTES, mSelectorPath, alphaMod); // Exclude the selector region, then draw minutes with no @@ -681,7 +693,7 @@ public class RadialTimePickerView extends View { canvas.save(Canvas.CLIP_SAVE_FLAG); canvas.clipPath(mSelectorPath, Region.Op.DIFFERENCE); drawTextElements(canvas, mTextSize[MINUTES], mTypeface, mTextColor[MINUTES], - mOuterTextMinutes, mOuterTextX[MINUTES], mOuterTextY[MINUTES], mPaint[MINUTES], + mMinutesText, mOuterTextX[MINUTES], mOuterTextY[MINUTES], mPaint[MINUTES], minutesAlpha, false, 0, false); canvas.restore(); @@ -690,7 +702,7 @@ public class RadialTimePickerView extends View { canvas.save(Canvas.CLIP_SAVE_FLAG); canvas.clipPath(mSelectorPath, Region.Op.INTERSECT); drawTextElements(canvas, mTextSize[MINUTES], mTypeface, mTextColor[MINUTES], - mOuterTextMinutes, mOuterTextX[MINUTES], mOuterTextY[MINUTES], mPaint[MINUTES], + mMinutesText, mOuterTextX[MINUTES], mOuterTextY[MINUTES], mPaint[MINUTES], minutesAlpha, true, mSelectionDegrees[MINUTES], true); canvas.restore(); } @@ -718,7 +730,7 @@ public class RadialTimePickerView extends View { // Calculate the current radius at which to place the selection circle. final int selRadius = mSelectorRadius; final int selLength = mCircleRadius - mTextInset[index]; - final double selAngleRad = Math.toRadians(mSelectionDegrees[index]); + final double selAngleRad = Math.toRadians(mSelectionDegrees[index % 2]); final float selCenterX = mXCenter + selLength * (float) Math.sin(selAngleRad); final float selCenterY = mYCenter - selLength * (float) Math.cos(selAngleRad); @@ -734,10 +746,10 @@ public class RadialTimePickerView extends View { } // Draw the dot if we're between two items. - final boolean shouldDrawDot = mSelectionDegrees[index] % 30 != 0; + final boolean shouldDrawDot = mSelectionDegrees[index % 2] % 30 != 0; if (shouldDrawDot) { final Paint dotPaint = mPaintSelector[index % 2][SELECTOR_DOT]; - dotPaint.setColor(color); + dotPaint.setColor(mSelectorDotColor); canvas.drawCircle(selCenterX, selCenterY, mSelectorDotRadius, dotPaint); } @@ -898,56 +910,43 @@ public class RadialTimePickerView extends View { } private int getDegreesFromXY(float x, float y, boolean constrainOutside) { - final double hypotenuse = Math.sqrt( - (y - mYCenter) * (y - mYCenter) + (x - mXCenter) * (x - mXCenter)); + // Ensure the point is inside the touchable area. + final int innerBound; + final int outerBound; + if (mIs24HourMode && mShowHours) { + innerBound = mMinDistForInnerNumber; + outerBound = mMaxDistForOuterNumber; + } else { + final int index = mShowHours ? HOURS : MINUTES; + final int center = mCircleRadius - mTextInset[index]; + innerBound = center - mSelectorRadius; + outerBound = center + mSelectorRadius; + } - // Basic check if we're outside the range of the disk - if (constrainOutside && hypotenuse > mCircleRadius) { + final double dX = x - mXCenter; + final double dY = y - mYCenter; + final double distFromCenter = Math.sqrt(dX * dX + dY * dY); + if (distFromCenter < innerBound || constrainOutside && distFromCenter > outerBound) { return -1; } - // Check - if (mIs24HourMode && mShowHours) { - if (hypotenuse >= mMinHypotenuseForInnerNumber - && hypotenuse <= mHalfwayHypotenusePoint) { - mIsOnInnerCircle = true; - } else if ((hypotenuse <= mMaxHypotenuseForOuterNumber || !constrainOutside) - && hypotenuse >= mHalfwayHypotenusePoint) { - mIsOnInnerCircle = false; - } else { - return -1; - } + // Convert to degrees. + final int degrees = (int) (Math.toDegrees(Math.atan2(dY, dX) + Math.PI / 2) + 0.5); + if (degrees < 0) { + return degrees + 360; } else { - final int index = (mShowHours) ? HOURS : MINUTES; - final float length = (mCircleRadius - mTextInset[index]); - final int distanceToNumber = (int) (hypotenuse - length); - final int maxAllowedDistance = mTextInset[index]; - if (distanceToNumber < -maxAllowedDistance - || (constrainOutside && distanceToNumber > maxAllowedDistance)) { - return -1; - } + return degrees; } + } - final float opposite = Math.abs(y - mYCenter); - int degrees = (int) (Math.toDegrees(Math.asin(opposite / hypotenuse)) + 0.5); - - // Now we have to translate to the correct quadrant. - final boolean rightSide = (x > mXCenter); - final boolean topSide = (y < mYCenter); - if (rightSide) { - if (topSide) { - degrees = 90 - degrees; - } else { - degrees = 90 + degrees; - } - } else { - if (topSide) { - degrees = 270 + degrees; - } else { - degrees = 270 - degrees; - } + private boolean getInnerCircleFromXY(float x, float y) { + if (mIs24HourMode && mShowHours) { + final double dX = x - mXCenter; + final double dY = y - mYCenter; + final double distFromCenter = Math.sqrt(dX * dX + dY * dY); + return distFromCenter <= mHalfwayDist; } - return degrees; + return false; } boolean mChangedDuringTouch = false; @@ -987,34 +986,28 @@ public class RadialTimePickerView extends View { private boolean handleTouchInput( float x, float y, boolean forceSelection, boolean autoAdvance) { - // Calling getDegreesFromXY has side effects, so cache - // whether we used to be on the inner circle. - final boolean wasOnInnerCircle = mIsOnInnerCircle; + final boolean isOnInnerCircle = getInnerCircleFromXY(x, y); final int degrees = getDegreesFromXY(x, y, false); if (degrees == -1) { return false; } - final int[] selectionDegrees = mSelectionDegrees; final int type; final int newValue; final boolean valueChanged; if (mShowHours) { final int snapDegrees = snapOnly30s(degrees, 0) % 360; - valueChanged = selectionDegrees[HOURS] != snapDegrees - || selectionDegrees[HOURS_INNER] != snapDegrees - || wasOnInnerCircle != mIsOnInnerCircle; - - selectionDegrees[HOURS] = snapDegrees; - selectionDegrees[HOURS_INNER] = snapDegrees; + valueChanged = mIsOnInnerCircle != isOnInnerCircle + || mSelectionDegrees[HOURS] != snapDegrees; + mIsOnInnerCircle = isOnInnerCircle; + mSelectionDegrees[HOURS] = snapDegrees; type = HOURS; newValue = getCurrentHour(); } else { final int snapDegrees = snapPrefer30s(degrees) % 360; - valueChanged = selectionDegrees[MINUTES] != snapDegrees; - - selectionDegrees[MINUTES] = snapDegrees; + valueChanged = mSelectionDegrees[MINUTES] != snapDegrees; + mSelectionDegrees[MINUTES] = snapDegrees; type = MINUTES; newValue = getCurrentMinute(); } @@ -1132,17 +1125,11 @@ public class RadialTimePickerView extends View { @Override protected int getVirtualViewAt(float x, float y) { final int id; - - // Calling getDegreesXY() has side-effects, so we need to cache the - // current inner circle value and restore after the call. - final boolean wasOnInnerCircle = mIsOnInnerCircle; final int degrees = getDegreesFromXY(x, y, true); - final boolean isOnInnerCircle = mIsOnInnerCircle; - mIsOnInnerCircle = wasOnInnerCircle; - if (degrees != -1) { final int snapDegrees = snapOnly30s(degrees, 0) % 360; if (mShowHours) { + final boolean isOnInnerCircle = getInnerCircleFromXY(x, y); final int hour24 = getHourForDegrees(snapDegrees, isOnInnerCircle); final int hour = mIs24HourMode ? hour24 : hour24To12(hour24); id = makeId(TYPE_HOUR, hour); @@ -1153,8 +1140,10 @@ public class RadialTimePickerView extends View { // If the touched minute is closer to the current minute // than it is to the snapped minute, return current. + final int currentOffset = getCircularDiff(current, touched, MINUTES_IN_HOUR); + final int snappedOffset = getCircularDiff(snapped, touched, MINUTES_IN_HOUR); final int minute; - if (Math.abs(current - touched) < Math.abs(snapped - touched)) { + if (currentOffset < snappedOffset) { minute = current; } else { minute = snapped; @@ -1168,6 +1157,20 @@ public class RadialTimePickerView extends View { return id; } + /** + * Returns the difference in degrees between two values along a circle. + * + * @param first value in the range [0,max] + * @param second value in the range [0,max] + * @param max the maximum value along the circle + * @return the difference in between the two values + */ + private int getCircularDiff(int first, int second, int max) { + final int diff = Math.abs(first - second); + final int midpoint = max / 2; + return (diff > midpoint) ? (max - diff) : diff; + } + @Override protected void getVisibleVirtualViews(IntArray virtualViewIds) { if (mShowHours) { @@ -1178,7 +1181,7 @@ public class RadialTimePickerView extends View { } } else { final int current = getCurrentMinute(); - for (int i = 0; i < 60; i += MINUTE_INCREMENT) { + for (int i = 0; i < MINUTES_IN_HOUR; i += MINUTE_INCREMENT) { virtualViewIds.add(makeId(TYPE_MINUTE, i)); // If the current minute falls between two increments, @@ -1236,7 +1239,7 @@ public class RadialTimePickerView extends View { if (value < current && nextValue > current) { // The current value is between two snap values. return makeId(type, current); - } else if (nextValue < 60) { + } else if (nextValue < MINUTES_IN_HOUR) { return makeId(type, nextValue); } } @@ -1290,7 +1293,7 @@ public class RadialTimePickerView extends View { final float centerRadius; final float degrees; if (type == TYPE_HOUR) { - final boolean innerCircle = mIs24HourMode && value > 0 && value <= 12; + final boolean innerCircle = getInnerCircleForHour(value); if (innerCircle) { centerRadius = mCircleRadius - mTextInset[HOURS_INNER]; radius = mSelectorRadius; diff --git a/core/java/com/android/internal/app/DumpHeapActivity.java b/core/java/com/android/internal/app/DumpHeapActivity.java index 7e70b0c..0ce501e 100644 --- a/core/java/com/android/internal/app/DumpHeapActivity.java +++ b/core/java/com/android/internal/app/DumpHeapActivity.java @@ -17,13 +17,16 @@ package com.android.internal.app; import android.app.Activity; +import android.app.ActivityManager; import android.app.AlertDialog; +import android.content.ActivityNotFoundException; import android.content.ClipData; import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.util.DebugUtils; +import android.util.Slog; /** * This activity is displayed when the system has collected a heap dump from @@ -34,6 +37,8 @@ public class DumpHeapActivity extends Activity { public static final String KEY_PROCESS = "process"; /** The size limit the process reached */ public static final String KEY_SIZE = "size"; + /** Optional name of package to directly launch */ + public static final String KEY_DIRECT_LAUNCH = "direct_launch"; // Broadcast action to determine when to delete the current dump heap data. public static final String ACTION_DELETE_DUMPHEAP = "com.android.server.am.DELETE_DUMPHEAP"; @@ -54,6 +59,28 @@ public class DumpHeapActivity extends Activity { mProcess = getIntent().getStringExtra(KEY_PROCESS); mSize = getIntent().getLongExtra(KEY_SIZE, 0); + + String directLaunch = getIntent().getStringExtra(KEY_DIRECT_LAUNCH); + if (directLaunch != null) { + Intent intent = new Intent(ActivityManager.ACTION_REPORT_HEAP_LIMIT); + intent.setPackage(directLaunch); + ClipData clip = ClipData.newUri(getContentResolver(), "Heap Dump", JAVA_URI); + intent.setClipData(clip); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.setType(clip.getDescription().getMimeType(0)); + intent.putExtra(Intent.EXTRA_STREAM, JAVA_URI); + try { + startActivity(intent); + scheduleDelete(); + mHandled = true; + finish(); + return; + } catch (ActivityNotFoundException e) { + Slog.i("DumpHeapActivity", "Unable to direct launch to " + directLaunch + + ": " + e.getMessage()); + } + } + AlertDialog.Builder b = new AlertDialog.Builder(this, android.R.style.Theme_Material_Light_Dialog_Alert); b.setTitle(com.android.internal.R.string.dump_heap_title); @@ -71,9 +98,7 @@ public class DumpHeapActivity extends Activity { @Override public void onClick(DialogInterface dialog, int which) { mHandled = true; - Intent broadcast = new Intent(ACTION_DELETE_DUMPHEAP); - broadcast.putExtra(EXTRA_DELAY_DELETE, true); - sendBroadcast(broadcast); + scheduleDelete(); Intent intent = new Intent(Intent.ACTION_SEND); ClipData clip = ClipData.newUri(getContentResolver(), "Heap Dump", JAVA_URI); intent.setClipData(clip); @@ -88,6 +113,12 @@ public class DumpHeapActivity extends Activity { mDialog = b.show(); } + void scheduleDelete() { + Intent broadcast = new Intent(ACTION_DELETE_DUMPHEAP); + broadcast.putExtra(EXTRA_DELAY_DELETE, true); + sendBroadcast(broadcast); + } + @Override protected void onStop() { super.onStop(); diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 49d565d..70f7b72 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -32,6 +32,7 @@ import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; import android.system.StructPollfd; +import android.text.Hyphenator; import android.util.EventLog; import android.util.Log; import android.webkit.WebViewFactory; @@ -182,6 +183,7 @@ public class ZygoteInit { preloadResources(); preloadOpenGL(); preloadSharedLibraries(); + preloadTextResources(); // Ask the WebViewFactory to do any initialization that must run in the zygote process, // for memory sharing purposes. WebViewFactory.prepareWebViewInZygote(); @@ -201,6 +203,10 @@ public class ZygoteInit { } } + private static void preloadTextResources() { + Hyphenator.init(); + } + /** * Performs Zygote process initialization. Loads and initializes * commonly used classes. diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index 873b516..4906f59 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -577,7 +577,8 @@ public: Layout layout; TypefaceImpl* typeface = getNativeTypeface(env, jpaint); - MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, textArray, index, count, textLength); + MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, textArray + index, 0, count, + count); result = layout.getAdvance(); env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT); return result; |
