diff options
168 files changed, 2619 insertions, 643 deletions
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java index 070e10509e4e..787fbdb89062 100644 --- a/cmds/content/src/com/android/commands/content/Content.java +++ b/cmds/content/src/com/android/commands/content/Content.java @@ -83,14 +83,14 @@ public class Content { + " Example:\n" + " # Change \"new_setting\" secure setting to \"newer_value\".\n" + " adb shell content update --uri content://settings/secure --bind" - + " value:s:newer_value --where \"name=\\'new_setting\\'\"\n" + + " value:s:newer_value --where \"name=\'new_setting\'\"\n" + "\n" + "usage: adb shell content delete --uri <URI> [--user <USER_ID>] --bind <BINDING>" + " [--bind <BINDING>...] [--where <WHERE>]\n" + " Example:\n" + " # Remove \"new_setting\" secure setting.\n" + " adb shell content delete --uri content://settings/secure " - + "--where \"name=\\'new_setting\\'\"\n" + + "--where \"name=\'new_setting\'\"\n" + "\n" + "usage: adb shell content query --uri <URI> [--user <USER_ID>]" + " [--projection <PROJECTION>] [--where <WHERE>] [--sort <SORT_ORDER>]\n" @@ -101,7 +101,7 @@ public class Content { + " # Select \"name\" and \"value\" columns from secure settings where \"name\" is " + "equal to \"new_setting\" and sort the result by name in ascending order.\n" + " adb shell content query --uri content://settings/secure --projection name:value" - + " --where \"name=\\'new_setting\\'\" --sort \"name ASC\"\n" + + " --where \"name=\'new_setting\'\" --sort \"name ASC\"\n" + "\n"; private static class Parser { diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 67d393043668..61b206756c68 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1701,6 +1701,21 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case GET_INTENT_FOR_INTENT_SENDER_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IIntentSender r = IIntentSender.Stub.asInterface( + data.readStrongBinder()); + Intent intent = getIntentForIntentSender(r); + reply.writeNoException(); + if (intent != null) { + reply.writeInt(1); + intent.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); + } else { + reply.writeInt(0); + } + return true; + } + case UPDATE_PERSISTENT_CONFIGURATION_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); Configuration config = Configuration.CREATOR.createFromParcel(data); @@ -3977,6 +3992,20 @@ class ActivityManagerProxy implements IActivityManager return res; } + public Intent getIntentForIntentSender(IIntentSender sender) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(sender.asBinder()); + mRemote.transact(GET_INTENT_FOR_INTENT_SENDER_TRANSACTION, data, reply, 0); + reply.readException(); + Intent res = reply.readInt() != 0 + ? Intent.CREATOR.createFromParcel(reply) : null; + data.recycle(); + reply.recycle(); + return res; + } + public void updatePersistentConfiguration(Configuration values) throws RemoteException { Parcel data = Parcel.obtain(); diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 8fc1c8666814..8af17a49ffdb 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -341,6 +341,8 @@ public interface IActivityManager extends IInterface { public boolean isIntentSenderAnActivity(IIntentSender sender) throws RemoteException; + public Intent getIntentForIntentSender(IIntentSender sender) throws RemoteException; + public void updatePersistentConfiguration(Configuration values) throws RemoteException; public long[] getProcessPss(int[] pids) throws RemoteException; @@ -621,4 +623,5 @@ public interface IActivityManager extends IInterface { int REQUEST_BUG_REPORT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+157; int INPUT_DISPATCHING_TIMED_OUT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+158; int CLEAR_PENDING_BACKUP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+159; + int GET_INTENT_FOR_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+160; } diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index d36d99deeb44..5c75affe09d9 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -790,6 +790,20 @@ public final class PendingIntent implements Parcelable { } /** + * @hide + * Return the Intent of this PendingIntent. + */ + public Intent getIntent() { + try { + return ActivityManagerNative.getDefault() + .getIntentForIntentSender(mTarget); + } catch (RemoteException e) { + // Should never happen. + return null; + } + } + + /** * Comparison operator on two PendingIntent objects, such that true * is returned then they both represent the same operation from the * same package. This allows you to use {@link #getActivity}, diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java index cb61a71b147a..24fd2e4cc56e 100644 --- a/core/java/android/appwidget/AppWidgetHost.java +++ b/core/java/android/appwidget/AppWidgetHost.java @@ -224,6 +224,22 @@ public class AppWidgetHost { } } + /** + * Gets a list of all the appWidgetIds that are bound to the current host + * + * @hide + */ + public int[] getAppWidgetIds() { + try { + if (sService == null) { + bindService(); + } + return sService.getAppWidgetIdsForHost(mHostId); + } catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } + } + private static void checkCallerIsSystem() { int uid = Process.myUid(); if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) { diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java index 977b46161164..e4b4b97715f1 100644 --- a/core/java/android/content/SyncManager.java +++ b/core/java/android/content/SyncManager.java @@ -58,6 +58,7 @@ import android.util.Pair; import android.util.Slog; import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; import com.android.internal.util.IndentingPrintWriter; import com.google.android.collect.Lists; import com.google.android.collect.Maps; @@ -155,7 +156,7 @@ public class SyncManager { private SyncStorageEngine mSyncStorageEngine; - // @GuardedBy("mSyncQueue") + @GuardedBy("mSyncQueue") private final SyncQueue mSyncQueue; protected final ArrayList<ActiveSyncContext> mActiveSyncContexts = Lists.newArrayList(); diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java index 10e7bff785d4..bdc5a3feff34 100644 --- a/core/java/android/content/SyncStorageEngine.java +++ b/core/java/android/content/SyncStorageEngine.java @@ -16,6 +16,7 @@ package android.content; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastXmlSerializer; @@ -74,7 +75,7 @@ public class SyncStorageEngine extends Handler { private static final long DEFAULT_POLL_FREQUENCY_SECONDS = 60 * 60 * 24; // One day - // @VisibleForTesting + @VisibleForTesting static final long MILLIS_IN_4WEEKS = 1000L * 60 * 60 * 24 * 7 * 4; /** Enum value for a sync start event. */ diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java index 6def4a169703..a07a865c7b0e 100644 --- a/core/java/android/content/pm/RegisteredServicesCache.java +++ b/core/java/android/content/pm/RegisteredServicesCache.java @@ -34,6 +34,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.Xml; +import com.android.internal.annotations.GuardedBy; import com.android.internal.util.FastXmlSerializer; import com.google.android.collect.Lists; import com.google.android.collect.Maps; @@ -77,15 +78,15 @@ public abstract class RegisteredServicesCache<V> { private final Object mServicesLock = new Object(); - // @GuardedBy("mServicesLock") + @GuardedBy("mServicesLock") private boolean mPersistentServicesFileDidNotExist; - // @GuardedBy("mServicesLock") + @GuardedBy("mServicesLock") private final SparseArray<UserServices<V>> mUserServices = new SparseArray<UserServices<V>>(); private static class UserServices<V> { - // @GuardedBy("mServicesLock") + @GuardedBy("mServicesLock") public final Map<V, Integer> persistentServices = Maps.newHashMap(); - // @GuardedBy("mServicesLock") + @GuardedBy("mServicesLock") public Map<V, ServiceInfo<V>> services = null; } diff --git a/core/java/android/hardware/display/WifiDisplay.java b/core/java/android/hardware/display/WifiDisplay.java index 0138b1c51f16..2fd52b8f4e7a 100644 --- a/core/java/android/hardware/display/WifiDisplay.java +++ b/core/java/android/hardware/display/WifiDisplay.java @@ -107,6 +107,15 @@ public final class WifiDisplay implements Parcelable { && Objects.equal(mDeviceAlias, other.mDeviceAlias); } + /** + * Returns true if the other display is not null and has the same address as this one. + * Can be used to perform identity comparisons on displays ignoring properties + * that might change during a connection such as the name or alias. + */ + public boolean hasSameAddress(WifiDisplay other) { + return other != null && mDeviceAddress.equals(other.mDeviceAddress); + } + @Override public int hashCode() { // The address on its own should be sufficiently unique for hashing purposes. diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index 446bbf0509db..c7576057e196 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -21,6 +21,7 @@ import android.os.Parcelable; import android.os.SystemClock; import android.util.SparseBooleanArray; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.Objects; @@ -190,14 +191,14 @@ public class NetworkStats implements Parcelable { return clone; } - // @VisibleForTesting + @VisibleForTesting public NetworkStats addIfaceValues( String iface, long rxBytes, long rxPackets, long txBytes, long txPackets) { return addValues( iface, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets, 0L); } - // @VisibleForTesting + @VisibleForTesting public NetworkStats addValues(String iface, int uid, int set, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) { return addValues(new Entry( @@ -269,7 +270,7 @@ public class NetworkStats implements Parcelable { return size; } - // @VisibleForTesting + @VisibleForTesting public int internalSize() { return iface.length; } @@ -335,7 +336,7 @@ public class NetworkStats implements Parcelable { * Find first stats index that matches the requested parameters, starting * search around the hinted index as an optimization. */ - // @VisibleForTesting + @VisibleForTesting public int findIndexHinted(String iface, int uid, int set, int tag, int hintIndex) { for (int offset = 0; offset < size; offset++) { final int halfOffset = offset / 2; diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java index d8e53d523f07..d3839ad4355e 100644 --- a/core/java/android/net/NetworkTemplate.java +++ b/core/java/android/net/NetworkTemplate.java @@ -33,6 +33,7 @@ import android.content.res.Resources; import android.os.Parcel; import android.os.Parcelable; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Objects; /** @@ -63,7 +64,7 @@ public class NetworkTemplate implements Parcelable { private static boolean sForceAllNetworkTypes = false; - // @VisibleForTesting + @VisibleForTesting public static void forceAllNetworkTypes() { sForceAllNetworkTypes = true; } diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 54f2fe3740bc..9821824502b6 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -18,6 +18,8 @@ package android.os; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.Formatter; import java.util.List; import java.util.Map; @@ -1127,8 +1129,10 @@ public abstract class BatteryStats implements Parcelable { if (totalTimeMillis != 0) { sb.append(linePrefix); formatTimeMs(sb, totalTimeMillis); - if (name != null) sb.append(name); - sb.append(' '); + if (name != null) { + sb.append(name); + sb.append(' '); + } sb.append('('); sb.append(count); sb.append(" times)"); @@ -1440,8 +1444,21 @@ public abstract class BatteryStats implements Parcelable { } } + static final class TimerEntry { + final String mName; + final int mId; + final BatteryStats.Timer mTimer; + final long mTime; + TimerEntry(String name, int id, BatteryStats.Timer timer, long time) { + mName = name; + mId = id; + mTimer = timer; + mTime = time; + } + } + @SuppressWarnings("unused") - public final void dumpLocked(PrintWriter pw, String prefix, int which, int reqUid) { + public final void dumpLocked(PrintWriter pw, String prefix, final int which, int reqUid) { final long rawUptime = SystemClock.uptimeMillis() * 1000; final long rawRealtime = SystemClock.elapsedRealtime() * 1000; final long batteryUptime = getBatteryUptime(rawUptime); @@ -1516,19 +1533,43 @@ public abstract class BatteryStats implements Parcelable { long txTotal = 0; long fullWakeLockTimeTotalMicros = 0; long partialWakeLockTimeTotalMicros = 0; - + + final Comparator<TimerEntry> timerComparator = new Comparator<TimerEntry>() { + @Override + public int compare(TimerEntry lhs, TimerEntry rhs) { + long lhsTime = lhs.mTime; + long rhsTime = rhs.mTime; + if (lhsTime < rhsTime) { + return 1; + } + if (lhsTime > rhsTime) { + return -1; + } + return 0; + } + }; + if (reqUid < 0) { Map<String, ? extends BatteryStats.Timer> kernelWakelocks = getKernelWakelockStats(); if (kernelWakelocks.size() > 0) { + final ArrayList<TimerEntry> timers = new ArrayList<TimerEntry>(); for (Map.Entry<String, ? extends BatteryStats.Timer> ent : kernelWakelocks.entrySet()) { - + BatteryStats.Timer timer = ent.getValue(); + long totalTimeMillis = computeWakeLock(timer, batteryRealtime, which); + if (totalTimeMillis > 0) { + timers.add(new TimerEntry(ent.getKey(), 0, timer, totalTimeMillis)); + } + } + Collections.sort(timers, timerComparator); + for (int i=0; i<timers.size(); i++) { + TimerEntry timer = timers.get(i); String linePrefix = ": "; sb.setLength(0); sb.append(prefix); sb.append(" Kernel Wake lock "); - sb.append(ent.getKey()); - linePrefix = printWakeLock(sb, ent.getValue(), batteryRealtime, null, which, - linePrefix); + sb.append(timer.mName); + linePrefix = printWakeLock(sb, timer.mTimer, batteryRealtime, null, + which, linePrefix); if (!linePrefix.equals(": ")) { sb.append(" realtime"); // Only print out wake locks that were held @@ -1537,7 +1578,9 @@ public abstract class BatteryStats implements Parcelable { } } } - + + final ArrayList<TimerEntry> timers = new ArrayList<TimerEntry>(); + for (int iu = 0; iu < NU; iu++) { Uid u = uidStats.valueAt(iu); rxTotal += u.getTcpBytesReceived(which); @@ -1557,8 +1600,18 @@ public abstract class BatteryStats implements Parcelable { Timer partialWakeTimer = wl.getWakeTime(WAKE_TYPE_PARTIAL); if (partialWakeTimer != null) { - partialWakeLockTimeTotalMicros += partialWakeTimer.getTotalTimeLocked( + long totalTimeMicros = partialWakeTimer.getTotalTimeLocked( batteryRealtime, which); + if (totalTimeMicros > 0) { + if (reqUid < 0) { + // Only show the ordered list of all wake + // locks if the caller is not asking for data + // about a specific uid. + timers.add(new TimerEntry(ent.getKey(), u.getUid(), + partialWakeTimer, totalTimeMicros)); + } + partialWakeLockTimeTotalMicros += totalTimeMicros; + } } } } @@ -1571,7 +1624,7 @@ public abstract class BatteryStats implements Parcelable { sb.append(prefix); sb.append(" Total full wakelock time: "); formatTimeMs(sb, (fullWakeLockTimeTotalMicros + 500) / 1000); - sb.append(", Total partial waklock time: "); formatTimeMs(sb, + sb.append(", Total partial wakelock time: "); formatTimeMs(sb, (partialWakeLockTimeTotalMicros + 500) / 1000); pw.println(sb.toString()); @@ -1676,9 +1729,26 @@ public abstract class BatteryStats implements Parcelable { pw.println(getDischargeAmountScreenOnSinceCharge()); pw.print(prefix); pw.print(" Amount discharged while screen off: "); pw.println(getDischargeAmountScreenOffSinceCharge()); - pw.println(" "); + pw.println(); + } + + if (timers.size() > 0) { + Collections.sort(timers, timerComparator); + pw.print(prefix); pw.println(" All partial wake locks:"); + for (int i=0; i<timers.size(); i++) { + TimerEntry timer = timers.get(i); + sb.setLength(0); + sb.append(" Wake lock #"); + sb.append(timer.mId); + sb.append(" "); + sb.append(timer.mName); + printWakeLock(sb, timer.mTimer, batteryRealtime, null, which, ": "); + sb.append(" realtime"); + pw.println(sb.toString()); + } + timers.clear(); + pw.println(); } - for (int iu=0; iu<NU; iu++) { final int uid = uidStats.keyAt(iu); diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 88529f8924e9..1bada67c4aad 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -22,6 +22,8 @@ import android.os.storage.StorageVolume; import android.text.TextUtils; import android.util.Log; +import com.android.internal.annotations.GuardedBy; + import java.io.File; /** @@ -47,7 +49,7 @@ public class Environment { private static final Object sLock = new Object(); - // @GuardedBy("sLock") + @GuardedBy("sLock") private static volatile StorageVolume sPrimaryVolume; private static StorageVolume getPrimaryVolume() { diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java index 3e90dfc5e901..ec660ee8ef29 100644 --- a/core/java/android/os/ParcelFileDescriptor.java +++ b/core/java/android/os/ParcelFileDescriptor.java @@ -15,6 +15,9 @@ */ package android.os; + +import dalvik.system.CloseGuard; + import java.io.Closeable; import java.io.File; import java.io.FileDescriptor; @@ -31,12 +34,16 @@ import java.net.Socket; */ public class ParcelFileDescriptor implements Parcelable, Closeable { private final FileDescriptor mFileDescriptor; - private boolean mClosed; - //this field is to create wrapper for ParcelFileDescriptor using another - //PartialFileDescriptor but avoid invoking close twice - //consider ParcelFileDescriptor A(fileDescriptor fd), ParcelFileDescriptor B(A) - //in this particular case fd.close might be invoked twice. - private final ParcelFileDescriptor mParcelDescriptor; + + /** + * Wrapped {@link ParcelFileDescriptor}, if any. Used to avoid + * double-closing {@link #mFileDescriptor}. + */ + private final ParcelFileDescriptor mWrapped; + + private volatile boolean mClosed; + + private final CloseGuard mGuard = CloseGuard.get(); /** * For use with {@link #open}: if {@link #MODE_CREATE} has been supplied @@ -289,13 +296,15 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { if (mClosed) { throw new IllegalStateException("Already closed"); } - if (mParcelDescriptor != null) { - int fd = mParcelDescriptor.detachFd(); + if (mWrapped != null) { + int fd = mWrapped.detachFd(); mClosed = true; + mGuard.close(); return fd; } int fd = getFd(); mClosed = true; + mGuard.close(); Parcel.clearFileDescriptor(mFileDescriptor); return fd; } @@ -307,15 +316,16 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { * @throws IOException * If an error occurs attempting to close this ParcelFileDescriptor. */ + @Override public void close() throws IOException { - synchronized (this) { - if (mClosed) return; - mClosed = true; - } - if (mParcelDescriptor != null) { + if (mClosed) return; + mClosed = true; + mGuard.close(); + + if (mWrapped != null) { // If this is a proxy to another file descriptor, just call through to its // close method. - mParcelDescriptor.close(); + mWrapped.close(); } else { Parcel.closeFileDescriptor(mFileDescriptor); } @@ -374,6 +384,9 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { @Override protected void finalize() throws Throwable { + if (mGuard != null) { + mGuard.warnIfOpen(); + } try { if (!mClosed) { close(); @@ -384,21 +397,22 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { } public ParcelFileDescriptor(ParcelFileDescriptor descriptor) { - super(); - mParcelDescriptor = descriptor; - mFileDescriptor = mParcelDescriptor.mFileDescriptor; + mWrapped = descriptor; + mFileDescriptor = mWrapped.mFileDescriptor; + mGuard.open("close"); } - /*package */ParcelFileDescriptor(FileDescriptor descriptor) { - super(); + /** {@hide} */ + public ParcelFileDescriptor(FileDescriptor descriptor) { if (descriptor == null) { throw new NullPointerException("descriptor must not be null"); } + mWrapped = null; mFileDescriptor = descriptor; - mParcelDescriptor = null; + mGuard.open("close"); } - /* Parcelable interface */ + @Override public int describeContents() { return Parcelable.CONTENTS_FILE_DESCRIPTOR; } @@ -408,6 +422,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { * If {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} is set in flags, * the file descriptor will be closed after a copy is written to the Parcel. */ + @Override public void writeToParcel(Parcel out, int flags) { out.writeFileDescriptor(mFileDescriptor); if ((flags&PARCELABLE_WRITE_RETURN_VALUE) != 0 && !mClosed) { @@ -421,12 +436,14 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { public static final Parcelable.Creator<ParcelFileDescriptor> CREATOR = new Parcelable.Creator<ParcelFileDescriptor>() { + @Override public ParcelFileDescriptor createFromParcel(Parcel in) { return in.readFileDescriptor(); } + + @Override public ParcelFileDescriptor[] newArray(int size) { return new ParcelFileDescriptor[size]; } }; - } diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 4a011135aeac..736762f67ff3 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -182,6 +182,8 @@ public final class PowerManager { * </p><p> * Since not all devices have proximity sensors, use {@link #isWakeLockLevelSupported} * to determine whether this wake lock level is supported. + * </p><p> + * Cannot be used with {@link #ACQUIRE_CAUSES_WAKEUP}. * </p> * * {@hide} diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java index 1060bd84d1f6..bcce61dc00aa 100644 --- a/core/java/android/text/format/DateUtils.java +++ b/core/java/android/text/format/DateUtils.java @@ -607,6 +607,30 @@ public class DateUtils } /** + * Return given duration in a human-friendly format. For example, "4 + * minutes" or "1 second". Returns only largest meaningful unit of time, + * from seconds up to hours. + * + * @hide + */ + public static CharSequence formatDuration(long millis) { + final Resources res = Resources.getSystem(); + if (millis >= HOUR_IN_MILLIS) { + final int hours = (int) ((millis + 1800000) / HOUR_IN_MILLIS); + return res.getQuantityString( + com.android.internal.R.plurals.duration_hours, hours, hours); + } else if (millis >= MINUTE_IN_MILLIS) { + final int minutes = (int) ((millis + 30000) / MINUTE_IN_MILLIS); + return res.getQuantityString( + com.android.internal.R.plurals.duration_minutes, minutes, minutes); + } else { + final int seconds = (int) ((millis + 500) / SECOND_IN_MILLIS); + return res.getQuantityString( + com.android.internal.R.plurals.duration_seconds, seconds, seconds); + } + } + + /** * Formats an elapsed time in the form "MM:SS" or "H:MM:SS" * for display on the call-in-progress screen. * @param elapsedSeconds the elapsed time in seconds. diff --git a/core/java/android/view/SimulatedTrackball.java b/core/java/android/view/SimulatedTrackball.java index bd472cfaff38..b9173718c105 100644 --- a/core/java/android/view/SimulatedTrackball.java +++ b/core/java/android/view/SimulatedTrackball.java @@ -41,6 +41,8 @@ class SimulatedTrackball { // Where the cutoff is for determining an edge swipe private static final float EDGE_SWIPE_THRESHOLD = 0.9f; private static final int FLICK_MSG_ID = 313; + // TODO: Pass touch slop from the input device + private static final int TOUCH_SLOP = 30; // The position of the previous touchpad event private float mLastTouchpadXPosition; @@ -95,7 +97,7 @@ class SimulatedTrackball { mMinFlickDistanceSquared *= mMinFlickDistanceSquared; mFlickDecay = Float.parseFloat(SystemProperties.get( "persist.sys.vr_flick_decay", "1.3")); - mTouchSlop = ViewConfiguration.getTouchSlop(); + mTouchSlop = TOUCH_SLOP; mTouchSlopSquared = mTouchSlop * mTouchSlop; } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index ff44475a7e28..17476276389d 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -623,6 +623,7 @@ import java.util.concurrent.atomic.AtomicInteger; * @attr ref android.R.styleable#View_hapticFeedbackEnabled * @attr ref android.R.styleable#View_keepScreenOn * @attr ref android.R.styleable#View_layerType + * @attr ref android.R.styleable#View_layoutDirection * @attr ref android.R.styleable#View_longClickable * @attr ref android.R.styleable#View_minHeight * @attr ref android.R.styleable#View_minWidth @@ -660,6 +661,7 @@ import java.util.concurrent.atomic.AtomicInteger; * @attr ref android.R.styleable#View_soundEffectsEnabled * @attr ref android.R.styleable#View_tag * @attr ref android.R.styleable#View_textAlignment + * @attr ref android.R.styleable#View_textDirection * @attr ref android.R.styleable#View_transformPivotX * @attr ref android.R.styleable#View_transformPivotY * @attr ref android.R.styleable#View_translationX @@ -5854,6 +5856,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@link #LAYOUT_DIRECTION_RTL}, * {@link #LAYOUT_DIRECTION_INHERIT} or * {@link #LAYOUT_DIRECTION_LOCALE}. + * * @attr ref android.R.styleable#View_layoutDirection * * @hide @@ -5909,6 +5912,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * For compatibility, this will return {@link #LAYOUT_DIRECTION_LTR} if API version * is lower than {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}. + * + * @attr ref android.R.styleable#View_layoutDirection */ @ViewDebug.ExportedProperty(category = "layout", mapping = { @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LTR, to = "RESOLVED_DIRECTION_LTR"), @@ -16627,6 +16632,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@link #TEXT_DIRECTION_RTL}, * {@link #TEXT_DIRECTION_LOCALE} * + * @attr ref android.R.styleable#View_textDirection + * * @hide */ @ViewDebug.ExportedProperty(category = "text", mapping = { @@ -16656,6 +16663,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Resolution will be done if the value is set to TEXT_DIRECTION_INHERIT. The resolution * proceeds up the parent chain of the view to get the value. If there is no parent, then it will * return the default {@link #TEXT_DIRECTION_FIRST_STRONG}. + * + * @attr ref android.R.styleable#View_textDirection */ public void setTextDirection(int textDirection) { if (getRawTextDirection() != textDirection) { @@ -16684,6 +16693,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@link #TEXT_DIRECTION_LTR}, * {@link #TEXT_DIRECTION_RTL}, * {@link #TEXT_DIRECTION_LOCALE} + * + * @attr ref android.R.styleable#View_textDirection */ public int getTextDirection() { return (mPrivateFlags2 & PFLAG2_TEXT_DIRECTION_RESOLVED_MASK) >> PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT; @@ -16816,6 +16827,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@link #TEXT_ALIGNMENT_VIEW_START}, * {@link #TEXT_ALIGNMENT_VIEW_END} * + * @attr ref android.R.styleable#View_textAlignment + * * @hide */ @ViewDebug.ExportedProperty(category = "text", mapping = { @@ -16879,6 +16892,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@link #TEXT_ALIGNMENT_TEXT_END}, * {@link #TEXT_ALIGNMENT_VIEW_START}, * {@link #TEXT_ALIGNMENT_VIEW_END} + * + * @attr ref android.R.styleable#View_textAlignment */ @ViewDebug.ExportedProperty(category = "text", mapping = { @ViewDebug.IntToString(from = TEXT_ALIGNMENT_INHERIT, to = "INHERIT"), diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index b1a44c5a89bc..85972c3ff6fa 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -291,6 +291,7 @@ public class Editor { mErrorWasChanged = true; if (mError == null) { + setErrorIcon(null); if (mErrorPopup != null) { if (mErrorPopup.isShowing()) { mErrorPopup.dismiss(); @@ -299,21 +300,24 @@ public class Editor { mErrorPopup = null; } - setErrorIcon(null); - } else if (mTextView.isFocused()) { - showError(); + } else { setErrorIcon(icon); + if (mTextView.isFocused()) { + showError(); + } } } private void setErrorIcon(Drawable icon) { - final Drawables dr = mTextView.mDrawables; - if (dr != null) { - mTextView.setCompoundDrawables(dr.mDrawableLeft, dr.mDrawableTop, icon, - dr.mDrawableBottom); - } else { - mTextView.setCompoundDrawables(null, null, icon, null); + Drawables dr = mTextView.mDrawables; + if (dr == null) { + mTextView.mDrawables = dr = new Drawables(); } + dr.setErrorDrawable(icon, mTextView); + + mTextView.resetResolvedDrawables(); + mTextView.invalidate(); + mTextView.requestLayout(); } private void hideError() { @@ -321,15 +325,13 @@ public class Editor { if (mErrorPopup.isShowing()) { mErrorPopup.dismiss(); } - - setErrorIcon(null); } mShowErrorAfterAttach = false; } /** - * Returns the Y offset to make the pointy top of the error point + * Returns the X offset to make the pointy top of the error point * at the middle of the error icon. */ private int getErrorX() { @@ -340,8 +342,23 @@ public class Editor { final float scale = mTextView.getResources().getDisplayMetrics().density; final Drawables dr = mTextView.mDrawables; - return mTextView.getWidth() - mErrorPopup.getWidth() - mTextView.getPaddingRight() - - (dr != null ? dr.mDrawableSizeRight : 0) / 2 + (int) (25 * scale + 0.5f); + + final int layoutDirection = mTextView.getLayoutDirection(); + int errorX; + int offset; + switch (layoutDirection) { + default: + case View.LAYOUT_DIRECTION_LTR: + offset = - (dr != null ? dr.mDrawableSizeRight : 0) / 2 + (int) (25 * scale + 0.5f); + errorX = mTextView.getWidth() - mErrorPopup.getWidth() - + mTextView.getPaddingRight() + offset; + break; + case View.LAYOUT_DIRECTION_RTL: + offset = (dr != null ? dr.mDrawableSizeLeft : 0) / 2 - (int) (25 * scale + 0.5f); + errorX = mTextView.getPaddingLeft() + offset; + break; + } + return errorX; } /** @@ -358,16 +375,27 @@ public class Editor { mTextView.getCompoundPaddingBottom() - compoundPaddingTop; final Drawables dr = mTextView.mDrawables; - int icontop = compoundPaddingTop + - (vspace - (dr != null ? dr.mDrawableHeightRight : 0)) / 2; + + final int layoutDirection = mTextView.getLayoutDirection(); + int height; + switch (layoutDirection) { + default: + case View.LAYOUT_DIRECTION_LTR: + height = (dr != null ? dr.mDrawableHeightRight : 0); + break; + case View.LAYOUT_DIRECTION_RTL: + height = (dr != null ? dr.mDrawableHeightLeft : 0); + break; + } + + int icontop = compoundPaddingTop + (vspace - height) / 2; /* * The "2" is the distance between the point and the top edge * of the background. */ final float scale = mTextView.getResources().getDisplayMetrics().density; - return icontop + (dr != null ? dr.mDrawableHeightRight : 0) - mTextView.getHeight() - - (int) (2 * scale + 0.5f); + return icontop + height - mTextView.getHeight() - (int) (2 * scale + 0.5f); } void createInputContentTypeIfNeeded() { @@ -3726,7 +3754,7 @@ public class Editor { super(v, width, height); mView = v; // Make sure the TextView has a background set as it will be used the first time it is - // shown and positionned. Initialized with below background, which should have + // shown and positioned. Initialized with below background, which should have // dimensions identical to the above version for this to work (and is more likely). mPopupInlineErrorBackgroundId = getResourceId(mPopupInlineErrorBackgroundId, com.android.internal.R.styleable.Theme_errorMessageBackground); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 5d904004cf02..0a16a663f086 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -284,15 +284,144 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private TextUtils.TruncateAt mEllipsize; static class Drawables { + final static int DRAWABLE_NONE = -1; + final static int DRAWABLE_RIGHT = 0; + final static int DRAWABLE_LEFT = 1; + final Rect mCompoundRect = new Rect(); + Drawable mDrawableTop, mDrawableBottom, mDrawableLeft, mDrawableRight, - mDrawableStart, mDrawableEnd; + mDrawableStart, mDrawableEnd, mDrawableError, mDrawableTemp; + int mDrawableSizeTop, mDrawableSizeBottom, mDrawableSizeLeft, mDrawableSizeRight, - mDrawableSizeStart, mDrawableSizeEnd; + mDrawableSizeStart, mDrawableSizeEnd, mDrawableSizeError, mDrawableSizeTemp; + int mDrawableWidthTop, mDrawableWidthBottom, mDrawableHeightLeft, mDrawableHeightRight, - mDrawableHeightStart, mDrawableHeightEnd; + mDrawableHeightStart, mDrawableHeightEnd, mDrawableHeightError, mDrawableHeightTemp; + int mDrawablePadding; + + int mDrawableSaved = DRAWABLE_NONE; + + public void resolveWithLayoutDirection(int layoutDirection) { + switch(layoutDirection) { + case LAYOUT_DIRECTION_RTL: + if (mDrawableStart != null) { + mDrawableRight = mDrawableStart; + + mDrawableSizeRight = mDrawableSizeStart; + mDrawableHeightRight = mDrawableHeightStart; + } + if (mDrawableEnd != null) { + mDrawableLeft = mDrawableEnd; + + mDrawableSizeLeft = mDrawableSizeEnd; + mDrawableHeightLeft = mDrawableHeightEnd; + } + break; + + case LAYOUT_DIRECTION_LTR: + default: + if (mDrawableStart != null) { + mDrawableLeft = mDrawableStart; + + mDrawableSizeLeft = mDrawableSizeStart; + mDrawableHeightLeft = mDrawableHeightStart; + } + if (mDrawableEnd != null) { + mDrawableRight = mDrawableEnd; + + mDrawableSizeRight = mDrawableSizeEnd; + mDrawableHeightRight = mDrawableHeightEnd; + } + break; + } + applyErrorDrawableIfNeeded(layoutDirection); + updateDrawablesLayoutDirection(layoutDirection); + } + + private void updateDrawablesLayoutDirection(int layoutDirection) { + if (mDrawableLeft != null) { + mDrawableLeft.setLayoutDirection(layoutDirection); + } + if (mDrawableRight != null) { + mDrawableRight.setLayoutDirection(layoutDirection); + } + if (mDrawableTop != null) { + mDrawableTop.setLayoutDirection(layoutDirection); + } + if (mDrawableBottom != null) { + mDrawableBottom.setLayoutDirection(layoutDirection); + } + } + + public void setErrorDrawable(Drawable dr, TextView tv) { + if (mDrawableError != dr && mDrawableError != null) { + mDrawableError.setCallback(null); + } + mDrawableError = dr; + + final Rect compoundRect = mCompoundRect; + int[] state = tv.getDrawableState(); + + if (mDrawableError != null) { + mDrawableError.setState(state); + mDrawableError.copyBounds(compoundRect); + mDrawableError.setCallback(tv); + mDrawableSizeError = compoundRect.width(); + mDrawableHeightError = compoundRect.height(); + } else { + mDrawableSizeError = mDrawableHeightError = 0; + } + } + + private void applyErrorDrawableIfNeeded(int layoutDirection) { + // first restore the initial state if needed + switch (mDrawableSaved) { + case DRAWABLE_LEFT: + mDrawableLeft = mDrawableTemp; + mDrawableSizeLeft = mDrawableSizeTemp; + mDrawableHeightLeft = mDrawableHeightTemp; + break; + case DRAWABLE_RIGHT: + mDrawableRight = mDrawableTemp; + mDrawableSizeRight = mDrawableSizeTemp; + mDrawableHeightRight = mDrawableHeightTemp; + break; + case DRAWABLE_NONE: + default: + } + // then, if needed, assign the Error drawable to the correct location + if (mDrawableError != null) { + switch(layoutDirection) { + case LAYOUT_DIRECTION_RTL: + mDrawableSaved = DRAWABLE_LEFT; + + mDrawableTemp = mDrawableLeft; + mDrawableSizeTemp = mDrawableSizeLeft; + mDrawableHeightTemp = mDrawableHeightLeft; + + mDrawableLeft = mDrawableError; + mDrawableSizeLeft = mDrawableSizeError; + mDrawableHeightLeft = mDrawableHeightError; + break; + case LAYOUT_DIRECTION_LTR: + default: + mDrawableSaved = DRAWABLE_RIGHT; + + mDrawableTemp = mDrawableRight; + mDrawableSizeTemp = mDrawableSizeRight; + mDrawableHeightTemp = mDrawableHeightRight; + + mDrawableRight = mDrawableError; + mDrawableSizeRight = mDrawableSizeError; + mDrawableHeightRight = mDrawableHeightError; + break; + } + } + } } + Drawables mDrawables; private CharWrapper mCharWrapper; @@ -8229,9 +8358,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener TextDirectionHeuristic getTextDirectionHeuristic() { if (hasPasswordTransformationMethod()) { - // TODO: take care of the content direction to show the password text and dots justified - // to the left or to the right - return TextDirectionHeuristics.LOCALE; + // passwords fields should be LTR + return TextDirectionHeuristics.LTR; } // Always need to resolve layout direction first @@ -8264,63 +8392,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return; } mLastLayoutDirection = layoutDirection; - // No drawable to resolve - if (mDrawables == null) { - return; - } - // No relative drawable to resolve - if (mDrawables.mDrawableStart == null && mDrawables.mDrawableEnd == null) { - return; - } - - Drawables dr = mDrawables; - switch(layoutDirection) { - case LAYOUT_DIRECTION_RTL: - if (dr.mDrawableStart != null) { - dr.mDrawableRight = dr.mDrawableStart; - - dr.mDrawableSizeRight = dr.mDrawableSizeStart; - dr.mDrawableHeightRight = dr.mDrawableHeightStart; - } - if (dr.mDrawableEnd != null) { - dr.mDrawableLeft = dr.mDrawableEnd; - dr.mDrawableSizeLeft = dr.mDrawableSizeEnd; - dr.mDrawableHeightLeft = dr.mDrawableHeightEnd; - } - break; - - case LAYOUT_DIRECTION_LTR: - default: - if (dr.mDrawableStart != null) { - dr.mDrawableLeft = dr.mDrawableStart; - - dr.mDrawableSizeLeft = dr.mDrawableSizeStart; - dr.mDrawableHeightLeft = dr.mDrawableHeightStart; - } - if (dr.mDrawableEnd != null) { - dr.mDrawableRight = dr.mDrawableEnd; - - dr.mDrawableSizeRight = dr.mDrawableSizeEnd; - dr.mDrawableHeightRight = dr.mDrawableHeightEnd; - } - break; - } - updateDrawablesLayoutDirection(dr, layoutDirection); - } - - private void updateDrawablesLayoutDirection(Drawables dr, int layoutDirection) { - if (dr.mDrawableLeft != null) { - dr.mDrawableLeft.setLayoutDirection(layoutDirection); - } - if (dr.mDrawableRight != null) { - dr.mDrawableRight.setLayoutDirection(layoutDirection); - } - if (dr.mDrawableTop != null) { - dr.mDrawableTop.setLayoutDirection(layoutDirection); - } - if (dr.mDrawableBottom != null) { - dr.mDrawableBottom.setLayoutDirection(layoutDirection); + // Resolve drawables + if (mDrawables != null) { + mDrawables.resolveWithLayoutDirection(layoutDirection); } } @@ -8328,6 +8403,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @hide */ protected void resetResolvedDrawables() { + super.resetResolvedDrawables(); mLastLayoutDirection = -1; } diff --git a/core/java/com/android/internal/annotations/GuardedBy.java b/core/java/com/android/internal/annotations/GuardedBy.java new file mode 100644 index 000000000000..fc6194553f93 --- /dev/null +++ b/core/java/com/android/internal/annotations/GuardedBy.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation type used to mark a method or field that can only be accessed when + * holding the referenced lock. + */ +@Target({ ElementType.FIELD, ElementType.METHOD }) +@Retention(RetentionPolicy.CLASS) +public @interface GuardedBy { + String value(); +} diff --git a/core/java/com/android/internal/annotations/Immutable.java b/core/java/com/android/internal/annotations/Immutable.java new file mode 100644 index 000000000000..b424275f7a86 --- /dev/null +++ b/core/java/com/android/internal/annotations/Immutable.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation type used to mark a class which is immutable. + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.CLASS) +public @interface Immutable { +} diff --git a/core/java/com/android/internal/annotations/VisibleForTesting.java b/core/java/com/android/internal/annotations/VisibleForTesting.java new file mode 100644 index 000000000000..bc3121c74e5f --- /dev/null +++ b/core/java/com/android/internal/annotations/VisibleForTesting.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Denotes that the class, method or field has its visibility relaxed so + * that unit tests can access it. + * <p/> + * The <code>visibility</code> argument can be used to specific what the original + * visibility should have been if it had not been made public or package-private for testing. + * The default is to consider the element private. + */ +@Retention(RetentionPolicy.SOURCE) +public @interface VisibleForTesting { + /** + * Intended visibility if the element had not been made public or package-private for + * testing. + */ + enum Visibility { + /** The element should be considered protected. */ + PROTECTED, + /** The element should be considered package-private. */ + PACKAGE, + /** The element should be considered private. */ + PRIVATE + } + + /** + * Intended visibility if the element had not been made public or package-private for testing. + * If not specified, one should assume the element originally intended to be private. + */ + Visibility visibility() default Visibility.PRIVATE; +} diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl index cfb16fadbea5..b63ad62c0588 100644 --- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl +++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl @@ -38,6 +38,7 @@ interface IAppWidgetService { void deleteHost(int hostId); void deleteAllHosts(); RemoteViews getAppWidgetViews(int appWidgetId); + int[] getAppWidgetIdsForHost(int hostId); // // for AppWidgetManager diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java index 8b222f02a3ca..c517a68e2b90 100644 --- a/core/java/com/android/internal/net/NetworkStatsFactory.java +++ b/core/java/com/android/internal/net/NetworkStatsFactory.java @@ -25,6 +25,7 @@ import android.net.NetworkStats; import android.os.StrictMode; import android.os.SystemClock; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ProcFileReader; import java.io.File; @@ -53,7 +54,7 @@ public class NetworkStatsFactory { this(new File("/proc/")); } - // @VisibleForTesting + @VisibleForTesting public NetworkStatsFactory(File procRoot) { mStatsXtIfaceAll = new File(procRoot, "net/xt_qtaguid/iface_stat_all"); mStatsXtIfaceFmt = new File(procRoot, "net/xt_qtaguid/iface_stat_fmt"); diff --git a/core/java/com/android/internal/util/LocalLog.java b/core/java/com/android/internal/util/LocalLog.java new file mode 100644 index 000000000000..f0e6171562f9 --- /dev/null +++ b/core/java/com/android/internal/util/LocalLog.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util; + +import java.io.PrintWriter; +import java.util.ArrayList; + +import android.util.Slog; + +/** + * Helper class for logging serious issues, which also keeps a small + * snapshot of the logged events that can be printed later, such as part + * of a system service's dumpsys output. + * @hide + */ +public class LocalLog { + private final String mTag; + private final int mMaxLines = 20; + private final ArrayList<String> mLines = new ArrayList<String>(mMaxLines); + + public LocalLog(String tag) { + mTag = tag; + } + + public void w(String msg) { + synchronized (mLines) { + Slog.w(mTag, msg); + if (mLines.size() >= mMaxLines) { + mLines.remove(0); + } + mLines.add(msg); + } + } + + public boolean dump(PrintWriter pw, String header, String prefix) { + synchronized (mLines) { + if (mLines.size() <= 0) { + return false; + } + if (header != null) { + pw.println(header); + } + for (int i=0; i<mLines.size(); i++) { + if (prefix != null) { + pw.print(prefix); + } + pw.println(mLines.get(i)); + } + return true; + } + } +} diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 75fef2413806..5fa0b7899427 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -116,16 +116,6 @@ public class LockPatternUtils { public static final String KEYGUARD_SHOW_APPWIDGET = "showappwidget"; /** - * Options used to lock the device upon user switch. - */ - public static final Bundle USER_SWITCH_LOCK_OPTIONS = new Bundle(); - - static { - USER_SWITCH_LOCK_OPTIONS.putBoolean(KEYGUARD_SHOW_USER_SWITCHER, true); - USER_SWITCH_LOCK_OPTIONS.putBoolean(KEYGUARD_SHOW_SECURITY_CHALLENGE, true); - } - - /** * The bit in LOCK_BIOMETRIC_WEAK_FLAGS to be used to indicate whether liveliness should * be used */ diff --git a/core/res/res/drawable-hdpi/kg_add_widget.png b/core/res/res/drawable-hdpi/kg_add_widget.png Binary files differindex 723d97a3a981..68971a58c5ac 100644 --- a/core/res/res/drawable-hdpi/kg_add_widget.png +++ b/core/res/res/drawable-hdpi/kg_add_widget.png diff --git a/core/res/res/drawable-hdpi/kg_add_widget_disabled.png b/core/res/res/drawable-hdpi/kg_add_widget_disabled.png Binary files differnew file mode 100644 index 000000000000..f24cf6423ff9 --- /dev/null +++ b/core/res/res/drawable-hdpi/kg_add_widget_disabled.png diff --git a/core/res/res/drawable-hdpi/kg_add_widget_pressed.png b/core/res/res/drawable-hdpi/kg_add_widget_pressed.png Binary files differnew file mode 100644 index 000000000000..55112ca7ea9a --- /dev/null +++ b/core/res/res/drawable-hdpi/kg_add_widget_pressed.png diff --git a/core/res/res/drawable-ldrtl-hdpi/popup_inline_error.9.png b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error.9.png Binary files differnew file mode 100644 index 000000000000..8b43f4ee333c --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error.9.png diff --git a/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_above.9.png b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_above.9.png Binary files differnew file mode 100644 index 000000000000..20e9002008bb --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_above.9.png diff --git a/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_above_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_above_holo_dark.9.png Binary files differnew file mode 100644 index 000000000000..b5f397c46411 --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_above_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_above_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_above_holo_light.9.png Binary files differnew file mode 100644 index 000000000000..a04d6954d785 --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_above_holo_light.9.png diff --git a/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_holo_dark.9.png Binary files differnew file mode 100644 index 000000000000..8567b1f3a837 --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_holo_light.9.png Binary files differnew file mode 100644 index 000000000000..7d1754ce0ef7 --- /dev/null +++ b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_holo_light.9.png diff --git a/core/res/res/drawable-ldrtl-ldpi/popup_inline_error.9.png b/core/res/res/drawable-ldrtl-ldpi/popup_inline_error.9.png Binary files differnew file mode 100644 index 000000000000..d2efb6241bc7 --- /dev/null +++ b/core/res/res/drawable-ldrtl-ldpi/popup_inline_error.9.png diff --git a/core/res/res/drawable-ldrtl-ldpi/popup_inline_error_above.9.png b/core/res/res/drawable-ldrtl-ldpi/popup_inline_error_above.9.png Binary files differnew file mode 100644 index 000000000000..04d200dcb4c9 --- /dev/null +++ b/core/res/res/drawable-ldrtl-ldpi/popup_inline_error_above.9.png diff --git a/core/res/res/drawable-ldrtl-mdpi/popup_inline_error.9.png b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error.9.png Binary files differnew file mode 100644 index 000000000000..27e8d4fe420d --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error.9.png diff --git a/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_above.9.png b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_above.9.png Binary files differnew file mode 100644 index 000000000000..4ae2b91d1fbf --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_above.9.png diff --git a/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_above_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_above_holo_dark.9.png Binary files differnew file mode 100644 index 000000000000..8cc3b693f082 --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_above_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_above_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_above_holo_light.9.png Binary files differnew file mode 100644 index 000000000000..7a84200d72dd --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_above_holo_light.9.png diff --git a/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_holo_dark.9.png Binary files differnew file mode 100644 index 000000000000..8fc2e2ed9759 --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_holo_light.9.png Binary files differnew file mode 100644 index 000000000000..687a691ad698 --- /dev/null +++ b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_holo_light.9.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error.9.png b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error.9.png Binary files differnew file mode 100644 index 000000000000..db91a5660466 --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error.9.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_above.9.png b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_above.9.png Binary files differnew file mode 100644 index 000000000000..90820b5fa9a0 --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_above.9.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_above_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_above_holo_dark.9.png Binary files differnew file mode 100644 index 000000000000..598997518833 --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_above_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_above_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_above_holo_light.9.png Binary files differnew file mode 100644 index 000000000000..3b3f87d3af10 --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_above_holo_light.9.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_holo_dark.9.png Binary files differnew file mode 100644 index 000000000000..75baba28be9e --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_holo_dark.9.png diff --git a/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_holo_light.9.png Binary files differnew file mode 100644 index 000000000000..6c0203da995e --- /dev/null +++ b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/kg_add_widget.png b/core/res/res/drawable-mdpi/kg_add_widget.png Binary files differindex 5b0a5a485450..136ae1740410 100644 --- a/core/res/res/drawable-mdpi/kg_add_widget.png +++ b/core/res/res/drawable-mdpi/kg_add_widget.png diff --git a/core/res/res/drawable-mdpi/kg_add_widget_disabled.png b/core/res/res/drawable-mdpi/kg_add_widget_disabled.png Binary files differnew file mode 100644 index 000000000000..02e0f0ecc260 --- /dev/null +++ b/core/res/res/drawable-mdpi/kg_add_widget_disabled.png diff --git a/core/res/res/drawable-mdpi/kg_add_widget_pressed.png b/core/res/res/drawable-mdpi/kg_add_widget_pressed.png Binary files differnew file mode 100644 index 000000000000..34a7aaa535a2 --- /dev/null +++ b/core/res/res/drawable-mdpi/kg_add_widget_pressed.png diff --git a/core/res/res/drawable-xhdpi/kg_add_widget.png b/core/res/res/drawable-xhdpi/kg_add_widget.png Binary files differindex 9c84de2e1d40..ca48be2793df 100644 --- a/core/res/res/drawable-xhdpi/kg_add_widget.png +++ b/core/res/res/drawable-xhdpi/kg_add_widget.png diff --git a/core/res/res/drawable-xhdpi/kg_add_widget_disabled.png b/core/res/res/drawable-xhdpi/kg_add_widget_disabled.png Binary files differnew file mode 100644 index 000000000000..55fa1acc5806 --- /dev/null +++ b/core/res/res/drawable-xhdpi/kg_add_widget_disabled.png diff --git a/core/res/res/drawable-xhdpi/kg_add_widget_pressed.png b/core/res/res/drawable-xhdpi/kg_add_widget_pressed.png Binary files differnew file mode 100644 index 000000000000..4b86727bc899 --- /dev/null +++ b/core/res/res/drawable-xhdpi/kg_add_widget_pressed.png diff --git a/core/res/res/drawable/keyguard_add_widget_button.xml b/core/res/res/drawable/keyguard_add_widget_button.xml new file mode 100644 index 000000000000..c26f81dd9d66 --- /dev/null +++ b/core/res/res/drawable/keyguard_add_widget_button.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2012 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" android:drawable="@drawable/kg_add_widget_pressed" /> + <item android:state_enabled="false" android:drawable="@drawable/kg_add_widget_disabled" /> + <item android:drawable="@drawable/kg_add_widget" /> +</selector> diff --git a/core/res/res/layout/keyguard_add_widget.xml b/core/res/res/layout/keyguard_add_widget.xml index db166ac015ea..d043fdbf6e84 100644 --- a/core/res/res/layout/keyguard_add_widget.xml +++ b/core/res/res/layout/keyguard_add_widget.xml @@ -36,7 +36,7 @@ android:layout_height="wrap_content" android:layout_gravity="center" android:padding="24dp" - android:src="@drawable/kg_add_widget" + android:src="@drawable/keyguard_add_widget_button" android:contentDescription="@string/keyguard_accessibility_add_widget"/> </FrameLayout> </com.android.internal.policy.impl.keyguard.KeyguardWidgetFrame> diff --git a/core/res/res/layout/keyguard_pin_view.xml b/core/res/res/layout/keyguard_pin_view.xml index e494b6997d38..6a3b9e688629 100644 --- a/core/res/res/layout/keyguard_pin_view.xml +++ b/core/res/res/layout/keyguard_pin_view.xml @@ -39,6 +39,7 @@ android:layout_height="0dp" android:orientation="vertical" android:layout_weight="1" + android:layoutDirection="ltr" > <LinearLayout android:layout_width="match_parent" diff --git a/core/res/res/layout/keyguard_sim_pin_view.xml b/core/res/res/layout/keyguard_sim_pin_view.xml index 026b025091cd..6e6fe0853680 100644 --- a/core/res/res/layout/keyguard_sim_pin_view.xml +++ b/core/res/res/layout/keyguard_sim_pin_view.xml @@ -44,6 +44,7 @@ android:layout_height="0dp" android:orientation="vertical" android:layout_weight="1" + android:layoutDirection="ltr" > <LinearLayout android:layout_width="match_parent" diff --git a/core/res/res/layout/keyguard_sim_puk_view.xml b/core/res/res/layout/keyguard_sim_puk_view.xml index 28a9f9a3594d..0412fdc9dcad 100644 --- a/core/res/res/layout/keyguard_sim_puk_view.xml +++ b/core/res/res/layout/keyguard_sim_puk_view.xml @@ -45,6 +45,7 @@ android:layout_height="0dp" android:orientation="vertical" android:layout_weight="1" + android:layoutDirection="ltr" > <LinearLayout android:layout_width="match_parent" diff --git a/core/res/res/mipmap-xxhdpi/sym_def_app_icon.png b/core/res/res/mipmap-xxhdpi/sym_def_app_icon.png Binary files differnew file mode 100644 index 000000000000..e3f314440372 --- /dev/null +++ b/core/res/res/mipmap-xxhdpi/sym_def_app_icon.png diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 51d23e8abec4..0fa1b1345602 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"weke"</string> <string name="year" msgid="4001118221013892076">"jaar"</string> <string name="years" msgid="6881577717993213522">"jaar"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"1 sekonde"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> sekondes"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"1 minuut"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> minute"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"1 uur"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> ure"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Videoprobleem"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Hierdie video is nie geldig vir stroming na hierdie toestel nie."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Kan nie hierdie video speel nie."</string> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index f846ffd0c044..e326639efa52 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"ሳምንቶች"</string> <string name="year" msgid="4001118221013892076">"ዓመት"</string> <string name="years" msgid="6881577717993213522">"ዓመታት"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"1 ሰከንድ"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> ሰከንዶች"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"1 ደቂቃ"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> ደቂቃዎች"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"1 ሰዓት"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> ሰዓታት"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"የቪዲዮ ችግር"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"ይቅርታ፣ ይህ ቪዲዮ በዚህ መሣሪያ ለመልቀቅ ትክክል አይደለም።"</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"ይሄን ቪዲዮ ማጫወት አልተቻለም።"</string> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index a7c0c5084e4e..82902c493940 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"أسابيع"</string> <string name="year" msgid="4001118221013892076">"سنة"</string> <string name="years" msgid="6881577717993213522">"أعوام"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"ثانية واحدة"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> من الثواني"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"دقيقة واحدة"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> من الدقائق"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"ساعة واحدة"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> من الساعات"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"مشكلة في الفيديو"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"عذرًا، هذا الفيديو غير صالح للبث على هذا الجهاز."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"لا يمكنك تشغيل هذا الفيديو."</string> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 6ae68f9316e6..711dc46455bf 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"тыд."</string> <string name="year" msgid="4001118221013892076">"год"</string> <string name="years" msgid="6881577717993213522">"г."</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"1 секунда"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> с"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"1 хвіліна"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> хв."</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"1 гадзіна"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> гадз."</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Праблема з відэа"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Відэа не падыходзіць для патокавай перадачы на гэту прыладу."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Немагчыма прайграць гэта відэа."</string> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index 838f0cfd1a45..859614f9660f 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"седмици"</string> <string name="year" msgid="4001118221013892076">"година"</string> <string name="years" msgid="6881577717993213522">"години"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"1 секунда"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> секунди"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"1 минута"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> минути"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"1 час"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> часа"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Проблем с видеоклипа"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Този видеоклип не е валиден за поточно предаване към това устройство."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Този видеоклип не може да се пусне."</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index fdc95067b738..20ae9dd194a4 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -993,6 +993,12 @@ <string name="weeks" msgid="6509623834583944518">"setmanes"</string> <string name="year" msgid="4001118221013892076">"any"</string> <string name="years" msgid="6881577717993213522">"anys"</string> + <!-- no translation found for duration_seconds:one (6962015528372969481) --> + <!-- no translation found for duration_seconds:other (1886107766577166786) --> + <!-- no translation found for duration_minutes:one (4915414002546085617) --> + <!-- no translation found for duration_minutes:other (3165187169224908775) --> + <!-- no translation found for duration_hours:one (8917467491248809972) --> + <!-- no translation found for duration_hours:other (3863962854246773930) --> <string name="VideoView_error_title" msgid="3534509135438353077">"Problema amb el vídeo"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Aquest vídeo no és vàlid per a la reproducció en aquest dispositiu."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"No es pot reproduir aquest vídeo."</string> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 0afc74f90dff..f5ccf1e2b363 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"týd."</string> <string name="year" msgid="4001118221013892076">"rokem"</string> <string name="years" msgid="6881577717993213522">"lety"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"1 sekunda"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> s"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"1 minuta"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> min"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"1 hodina"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> h"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Potíže s videem"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Toto video nelze přenášet datovým proudem do tohoto zařízení."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Toto video nelze přehrát."</string> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index b0fcf8b75b2b..a3dd19af2088 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"uger"</string> <string name="year" msgid="4001118221013892076">"år"</string> <string name="years" msgid="6881577717993213522">"år"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"Ét sekund"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> sekunder"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"Ét minut"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> minutter"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"Én time"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> timer"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Videoproblem"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Denne video kan ikke streames på denne enhed."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Videoen kan ikke afspilles."</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index a32bbe6f1f08..48da50007441 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"Wochen"</string> <string name="year" msgid="4001118221013892076">"Jahr"</string> <string name="years" msgid="6881577717993213522">"Jahre"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"1 Sekunde"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> Sekunden"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"1 Minute"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> Minuten"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"1 Stunde"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> Stunden"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Videoprobleme"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Dieses Video ist nicht für Streaming auf diesem Gerät gültig."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Video kann nicht wiedergegeben werden."</string> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index 069c5d6bf297..9b3501591efe 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -993,6 +993,12 @@ <string name="weeks" msgid="6509623834583944518">"εβδομάδες"</string> <string name="year" msgid="4001118221013892076">"έτος"</string> <string name="years" msgid="6881577717993213522">"έτη"</string> + <!-- no translation found for duration_seconds:one (6962015528372969481) --> + <!-- no translation found for duration_seconds:other (1886107766577166786) --> + <!-- no translation found for duration_minutes:one (4915414002546085617) --> + <!-- no translation found for duration_minutes:other (3165187169224908775) --> + <!-- no translation found for duration_hours:one (8917467491248809972) --> + <!-- no translation found for duration_hours:other (3863962854246773930) --> <string name="VideoView_error_title" msgid="3534509135438353077">"Πρόβλημα με το βίντεο"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Αυτό το βίντεο δεν είναι έγκυρο για ροή σε αυτή τη συσκευή."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Δεν μπορείτε να αναπαράγετε αυτό το βίντεο."</string> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index 888e42e995d2..5ceae639cca1 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"weeks"</string> <string name="year" msgid="4001118221013892076">"year"</string> <string name="years" msgid="6881577717993213522">"years"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"1 second"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> seconds"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"1 minute"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> minutes"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"1 hour"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> hours"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Video problem"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"This video isn\'t valid for streaming to this device."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Can\'t play this video."</string> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index 47d436d53644..648f1f6de78d 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"semanas"</string> <string name="year" msgid="4001118221013892076">"año"</string> <string name="years" msgid="6881577717993213522">"años"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"1 segundo"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> segundos"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"1 minuto"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> minutos"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"1 hora"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> horas"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Problemas de video"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"No es posible transmitir este video al dispositivo."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"No se puede reproducir el video."</string> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index c129483569f2..fb8548d6dd16 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -993,6 +993,12 @@ <string name="weeks" msgid="6509623834583944518">"semanas"</string> <string name="year" msgid="4001118221013892076">"año"</string> <string name="years" msgid="6881577717993213522">"años"</string> + <!-- no translation found for duration_seconds:one (6962015528372969481) --> + <!-- no translation found for duration_seconds:other (1886107766577166786) --> + <!-- no translation found for duration_minutes:one (4915414002546085617) --> + <!-- no translation found for duration_minutes:other (3165187169224908775) --> + <!-- no translation found for duration_hours:one (8917467491248809972) --> + <!-- no translation found for duration_hours:other (3863962854246773930) --> <string name="VideoView_error_title" msgid="3534509135438353077">"Incidencias con el vídeo"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Este vídeo no se puede transmitir al dispositivo."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"No se puede reproducir el vídeo."</string> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index 5fb21d41c4c3..85167ceab03c 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"nädalat"</string> <string name="year" msgid="4001118221013892076">"aasta"</string> <string name="years" msgid="6881577717993213522">"aastat"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"1 sekund"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> sekundit"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"1 minut"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> minutit"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"1 tund"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> tundi"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Probleem videoga"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"See video ei sobi voogesituseks selles seadmes."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Videot ei saa esitada."</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index d5e624d0fa77..95d3bfa1edae 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -993,6 +993,12 @@ <string name="weeks" msgid="6509623834583944518">"هفته"</string> <string name="year" msgid="4001118221013892076">"سال"</string> <string name="years" msgid="6881577717993213522">"سال"</string> + <!-- no translation found for duration_seconds:one (6962015528372969481) --> + <!-- no translation found for duration_seconds:other (1886107766577166786) --> + <!-- no translation found for duration_minutes:one (4915414002546085617) --> + <!-- no translation found for duration_minutes:other (3165187169224908775) --> + <!-- no translation found for duration_hours:one (8917467491248809972) --> + <!-- no translation found for duration_hours:other (3863962854246773930) --> <string name="VideoView_error_title" msgid="3534509135438353077">"مشکل در ویدئو"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"متأسفیم، این ویدئو برای پخش جریانی با این دستگاه معتبر نیست."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"پخش این ویدئو ممکن نیست."</string> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index 2b08bea2ffcb..70f5dc5d8c7d 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"viikkoa"</string> <string name="year" msgid="4001118221013892076">"vuosi"</string> <string name="years" msgid="6881577717993213522">"vuotta"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"1 sekunti"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> sekuntia"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"1 minuutti"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> minuuttia"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"1 tunti"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> tuntia"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Video-ongelma"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Tätä videota ei voi suoratoistaa tällä laitteella."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Videota ei voida toistaa."</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 479fe1822ec5..c319c6dbf471 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"semaines"</string> <string name="year" msgid="4001118221013892076">"année"</string> <string name="years" msgid="6881577717993213522">"années"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"1 seconde"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> secondes"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"1 minute"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> minutes"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"1 heure"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> heures"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Problème vidéo"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Impossible de lire cette vidéo en streaming sur cet appareil."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Impossible de lire la vidéo."</string> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 65aa56357484..f272ef4d6cb8 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"सप्ताह"</string> <string name="year" msgid="4001118221013892076">"वर्ष"</string> <string name="years" msgid="6881577717993213522">"वर्ष"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"1 सेकंड"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> सेकंड"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"1 मिनट"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> मिनट"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"1 घंटा"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> घंटे"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"वीडियो समस्याएं"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"यह वीडियो इस उपकरण पर स्ट्रीमिंग के लिए मान्य नहीं है."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"यह वीडियो नहीं चलाया जा सकता."</string> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index e279216c8b4a..c41bb1dea8fc 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"tjedna"</string> <string name="year" msgid="4001118221013892076">"godina"</string> <string name="years" msgid="6881577717993213522">"godina"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"1 sekundu"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> s"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"1 minutu"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> min"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"1 sat"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> h"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Problem s videozapisom"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Ovaj videozapis nije valjan za streaming na ovaj uređaj."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Ovaj videozapis nije moguće reproducirati."</string> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index 88f4046fedc1..c53108c38e3a 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"hét"</string> <string name="year" msgid="4001118221013892076">"év"</string> <string name="years" msgid="6881577717993213522">"év"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"1 másodperc"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> másodperc"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"1 perc"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> perc"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"1 óra"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> óra"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Videoprobléma"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Ezt a videót nem lehet megjeleníteni ezen az eszközön."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Nem lehet lejátszani ezt a videót."</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index b5dfcd51170a..d281e039b0d7 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"minggu"</string> <string name="year" msgid="4001118221013892076">"tahun"</string> <string name="years" msgid="6881577717993213522">"tahun"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"1 detik"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> detik"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"1 menit"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> menit"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"1 jam"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> jam"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Masalah video"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Video ini tidak valid untuk pengaliran ke perangkat ini."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Tidak dapat memutar video ini."</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 0edb0c1348f9..92e7c955d92e 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"settimane"</string> <string name="year" msgid="4001118221013892076">"anno"</string> <string name="years" msgid="6881577717993213522">"anni"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"1 secondo"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> secondi"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"1 minuto"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> minuti"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"1 ora"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> ore"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Problemi video"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Questo video non è valido per lo streaming su questo dispositivo."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Impossibile riprodurre il video."</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index bb6a3ac17c7c..bfdca9f13425 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"שבועות"</string> <string name="year" msgid="4001118221013892076">"שנה"</string> <string name="years" msgid="6881577717993213522">"שנים"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"שנייה אחת"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> שניות"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"דקה אחת"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> דקות"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"שעה אחת"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> שעות"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"בעיה בווידאו"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"סרטון זה אינו חוקי להעברה כמדיה זורמת למכשיר זה."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"לא ניתן להפעיל סרטון זה."</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 8af0fed182c3..02126f0d6d5f 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -993,6 +993,12 @@ <string name="weeks" msgid="6509623834583944518">"週間"</string> <string name="year" msgid="4001118221013892076">"年"</string> <string name="years" msgid="6881577717993213522">"年"</string> + <!-- no translation found for duration_seconds:one (6962015528372969481) --> + <!-- no translation found for duration_seconds:other (1886107766577166786) --> + <!-- no translation found for duration_minutes:one (4915414002546085617) --> + <!-- no translation found for duration_minutes:other (3165187169224908775) --> + <!-- no translation found for duration_hours:one (8917467491248809972) --> + <!-- no translation found for duration_hours:other (3863962854246773930) --> <string name="VideoView_error_title" msgid="3534509135438353077">"動画の問題"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"この動画はこの端末にストリーミングできません。"</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"この動画を再生できません。"</string> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index 37c6b01fb53e..6bdb5d7878cf 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"주"</string> <string name="year" msgid="4001118221013892076">"년"</string> <string name="years" msgid="6881577717993213522">"년"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"1초"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g>초"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"1분"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g>분"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"1시간"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g>시간"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"영상 문제"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"이 기기로 스트리밍하기에 적합하지 않은 동영상입니다."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"동영상을 재생할 수 없습니다."</string> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index f2ad50441937..3e3c396611d8 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"sav."</string> <string name="year" msgid="4001118221013892076">"metai"</string> <string name="years" msgid="6881577717993213522">"metai"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"1 sek."</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> sek."</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"1 min."</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> min."</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"1 val."</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> val."</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Vaizdo įrašo problema"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Šis vaizdo įrašas netinkamas srautiniu būdu perduoti į šį įrenginį."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Negalima paleisti šio vaizdo įrašo."</string> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index ee0b023a9e68..dc4312f34c6d 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"nedēļas"</string> <string name="year" msgid="4001118221013892076">"gads"</string> <string name="years" msgid="6881577717993213522">"gadi"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"1 s"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> s"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"1 min"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> min"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"1 h"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> h"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Video problēma"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Šis video nav derīgs straumēšanai uz šo ierīci."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Nevar atskaņot šo video."</string> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index e89f70f4590d..5e649a7f261c 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"minggu"</string> <string name="year" msgid="4001118221013892076">"tahun"</string> <string name="years" msgid="6881577717993213522">"tahun"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"1 saat"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> saat"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"1 minit"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> minit"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"1 jam"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> jam"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Masalah video"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Maaf, video ini tidak sah untuk penstriman ke peranti ini."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Tidak dapat mainkan video ini."</string> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 42df589a4b31..79cf1eaa5e6f 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"uker"</string> <string name="year" msgid="4001118221013892076">"år"</string> <string name="years" msgid="6881577717993213522">"år"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"Ett sekund"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> sekunder"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"Ett minutt"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> minutter"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"Én time"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> timer"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Videoproblem"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Denne videoen er ikke gyldig for direkteavspilling på enheten."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Kan ikke spille av denne videoen."</string> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 21fe1cc1061c..50b3a876d2ca 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"weken"</string> <string name="year" msgid="4001118221013892076">"jaar"</string> <string name="years" msgid="6881577717993213522">"jaren"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"1 seconde"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> seconden"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"1 minuut"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> minuten"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"1 uur"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> uur"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Probleem met video"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Deze video kan niet worden gestreamd naar dit apparaat."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Deze video kan niet worden afgespeeld."</string> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index d0f1db3e79dd..3a9a0f67f60d 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -993,6 +993,12 @@ <string name="weeks" msgid="6509623834583944518">"tygodni"</string> <string name="year" msgid="4001118221013892076">"rok"</string> <string name="years" msgid="6881577717993213522">"lat"</string> + <!-- no translation found for duration_seconds:one (6962015528372969481) --> + <!-- no translation found for duration_seconds:other (1886107766577166786) --> + <!-- no translation found for duration_minutes:one (4915414002546085617) --> + <!-- no translation found for duration_minutes:other (3165187169224908775) --> + <!-- no translation found for duration_hours:one (8917467491248809972) --> + <!-- no translation found for duration_hours:other (3863962854246773930) --> <string name="VideoView_error_title" msgid="3534509135438353077">"Problem z filmem"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Ten film nie nadaje się do strumieniowego przesyłania do tego urządzenia."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Nie można odtworzyć tego filmu."</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index fd7211ea6e6f..f1ac978b066a 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"semanas"</string> <string name="year" msgid="4001118221013892076">"ano"</string> <string name="years" msgid="6881577717993213522">"anos"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"1 segundo"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> segundos"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"1 minuto"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> minutos"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"1 hora"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> horas"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Problema com o vídeo"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Este vídeo não é válido para transmissão em fluxo contínuo neste aparelho."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Não é possível reproduzir este vídeo."</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index ed656fe4e3fe..d4dd49455059 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"semanas"</string> <string name="year" msgid="4001118221013892076">"ano"</string> <string name="years" msgid="6881577717993213522">"anos"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"Um segundo"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> segundos"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"Um minuto"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> minutos"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"Uma hora"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> horas"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Problema com o vídeo"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Este vídeo não é válido para transmissão neste dispositivo."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Não é possível reproduzir este vídeo."</string> diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml index 0e7aaec5db2c..d3efd7e0dd73 100644 --- a/core/res/res/values-rm/strings.xml +++ b/core/res/res/values-rm/strings.xml @@ -1558,6 +1558,12 @@ <string name="weeks" msgid="6509623834583944518">"emnas"</string> <string name="year" msgid="4001118221013892076">"onn"</string> <string name="years" msgid="6881577717993213522">"onns"</string> + <!-- no translation found for duration_seconds:one (6962015528372969481) --> + <!-- no translation found for duration_seconds:other (1886107766577166786) --> + <!-- no translation found for duration_minutes:one (4915414002546085617) --> + <!-- no translation found for duration_minutes:other (3165187169224908775) --> + <!-- no translation found for duration_hours:one (8917467491248809972) --> + <!-- no translation found for duration_hours:other (3863962854246773930) --> <!-- no translation found for VideoView_error_title (3534509135438353077) --> <skip /> <!-- no translation found for VideoView_error_text_invalid_progressive_playback (3186670335938670444) --> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index f274acd0a9a3..d0ef77432333 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -993,6 +993,12 @@ <string name="weeks" msgid="6509623834583944518">"săptămâni"</string> <string name="year" msgid="4001118221013892076">"an"</string> <string name="years" msgid="6881577717993213522">"ani"</string> + <!-- no translation found for duration_seconds:one (6962015528372969481) --> + <!-- no translation found for duration_seconds:other (1886107766577166786) --> + <!-- no translation found for duration_minutes:one (4915414002546085617) --> + <!-- no translation found for duration_minutes:other (3165187169224908775) --> + <!-- no translation found for duration_hours:one (8917467491248809972) --> + <!-- no translation found for duration_hours:other (3863962854246773930) --> <string name="VideoView_error_title" msgid="3534509135438353077">"Problemă video"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Acest fişier video nu este valid pentru a fi transmis în flux către acest dispozitiv."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Nu puteţi reda acest videoclip"</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 28f044fd0565..9314866dd2a7 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -993,6 +993,12 @@ <string name="weeks" msgid="6509623834583944518">"нед."</string> <string name="year" msgid="4001118221013892076">"г."</string> <string name="years" msgid="6881577717993213522">"г."</string> + <!-- no translation found for duration_seconds:one (6962015528372969481) --> + <!-- no translation found for duration_seconds:other (1886107766577166786) --> + <!-- no translation found for duration_minutes:one (4915414002546085617) --> + <!-- no translation found for duration_minutes:other (3165187169224908775) --> + <!-- no translation found for duration_hours:one (8917467491248809972) --> + <!-- no translation found for duration_hours:other (3863962854246773930) --> <string name="VideoView_error_title" msgid="3534509135438353077">"Ошибка"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Это видео не предназначено для потокового воспроизведения на данном устройстве."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Не удалось воспроизвести видео."</string> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index c364380689eb..12a3b8fffae3 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -993,6 +993,12 @@ <string name="weeks" msgid="6509623834583944518">"týždne"</string> <string name="year" msgid="4001118221013892076">"rok"</string> <string name="years" msgid="6881577717993213522">"roky"</string> + <!-- no translation found for duration_seconds:one (6962015528372969481) --> + <!-- no translation found for duration_seconds:other (1886107766577166786) --> + <!-- no translation found for duration_minutes:one (4915414002546085617) --> + <!-- no translation found for duration_minutes:other (3165187169224908775) --> + <!-- no translation found for duration_hours:one (8917467491248809972) --> + <!-- no translation found for duration_hours:other (3863962854246773930) --> <string name="VideoView_error_title" msgid="3534509135438353077">"Problém s videom"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Je nám ľúto, ale toto video sa nedá streamovať do tohto zariadenia."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Toto video nie je možné prehrať."</string> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index 7f94c20440f3..3e9273d990ca 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"tednov"</string> <string name="year" msgid="4001118221013892076">"leto"</string> <string name="years" msgid="6881577717993213522">"let"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"1 sekunda"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> s"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"1 minuta"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> min"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"1 ura"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> h"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Težava z videoposnetkom"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Ta videoposnetek ni veljaven za pretakanje v to napravo."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Tega videoposnetka ni mogoče predvajati."</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 5a94aad6f2a8..53604f1b1111 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -993,6 +993,12 @@ <string name="weeks" msgid="6509623834583944518">"недеље(а)"</string> <string name="year" msgid="4001118221013892076">"година"</string> <string name="years" msgid="6881577717993213522">"годинe(а)"</string> + <!-- no translation found for duration_seconds:one (6962015528372969481) --> + <!-- no translation found for duration_seconds:other (1886107766577166786) --> + <!-- no translation found for duration_minutes:one (4915414002546085617) --> + <!-- no translation found for duration_minutes:other (3165187169224908775) --> + <!-- no translation found for duration_hours:one (8917467491248809972) --> + <!-- no translation found for duration_hours:other (3863962854246773930) --> <string name="VideoView_error_title" msgid="3534509135438353077">"Проблем са видео снимком"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Овај видео не може да се стримује на овом уређају."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Не можете да пустите овај видео."</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index 2737d62d9c46..d6a8eeb62654 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"veckor"</string> <string name="year" msgid="4001118221013892076">"år"</string> <string name="years" msgid="6881577717993213522">"år"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"1 sekund"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> sekunder"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"1 minut"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> minuter"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"1 timme"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> timmar"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Videoproblem"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Videon kan tyvärr inte spelas upp i den här enheten."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Det går inte att spela upp videon."</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index 5fc2a136b144..14f2b223721d 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"wiki"</string> <string name="year" msgid="4001118221013892076">"mwaka"</string> <string name="years" msgid="6881577717993213522">"miaka"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"Sekunde 1"</item> + <item quantity="other" msgid="1886107766577166786">"Sekunde <xliff:g id="COUNT">%d</xliff:g>"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"Dakika 1"</item> + <item quantity="other" msgid="3165187169224908775">"Dakika <xliff:g id="COUNT">%d</xliff:g>"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"Saa 1"</item> + <item quantity="other" msgid="3863962854246773930">"Saa <xliff:g id="COUNT">%d</xliff:g>"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Shida ya video"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Video hii si halali kutiririshwa kwa kifaa hiki."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Haiwezi kucheza video hii."</string> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index 0a86a866c150..d26b2d53f0d9 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -993,6 +993,12 @@ <string name="weeks" msgid="6509623834583944518">"สัปดาห์"</string> <string name="year" msgid="4001118221013892076">"ปี"</string> <string name="years" msgid="6881577717993213522">" ปี"</string> + <!-- no translation found for duration_seconds:one (6962015528372969481) --> + <!-- no translation found for duration_seconds:other (1886107766577166786) --> + <!-- no translation found for duration_minutes:one (4915414002546085617) --> + <!-- no translation found for duration_minutes:other (3165187169224908775) --> + <!-- no translation found for duration_hours:one (8917467491248809972) --> + <!-- no translation found for duration_hours:other (3863962854246773930) --> <string name="VideoView_error_title" msgid="3534509135438353077">"ปัญหาเกี่ยวกับวิดีโอ"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"วิดีโอนี้ไม่สามารถสตรีมไปยังอุปกรณ์นี้"</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"ไม่สามารถเล่นวิดีโอนี้"</string> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index 072f6df653a4..3d68842bb503 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"mga linggo"</string> <string name="year" msgid="4001118221013892076">"taon"</string> <string name="years" msgid="6881577717993213522">"mga taon"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"1 segundo"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> (na) segundo"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"1 minuto"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> (na) minuto"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"1 oras"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> (na) oras"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Problema sa video"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Hindi wasto ang video na ito para sa streaming sa device na ito."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Hindi ma-play ang video na ito."</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index dbb7b0d26ded..d7752333f2e6 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"hafta"</string> <string name="year" msgid="4001118221013892076">"yıl"</string> <string name="years" msgid="6881577717993213522">"yıl"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"1 saniye"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> saniye"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"1 dakika"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> dakika"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"1 saat"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> saat"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Video sorunu"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Bu video bu cihazda akış için uygun değil."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Bu video oynatılamıyor."</string> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index 65120074afc9..cc33b7bb6722 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"тижн."</string> <string name="year" msgid="4001118221013892076">"рік"</string> <string name="years" msgid="6881577717993213522">"р."</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"1 с"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> с"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"1 хв"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> хв"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"1 год"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> год"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Проблема з відео"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Відео не придатне для потокового передавання в цей пристрій."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Неможливо відтворити це відео."</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index 7fb341296b25..3e47de59fcfb 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"tuần"</string> <string name="year" msgid="4001118221013892076">"năm"</string> <string name="years" msgid="6881577717993213522">"năm"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"1 giây"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> giây"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"1 phút"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> phút"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"1 giờ"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> giờ"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"Sự cố video"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Video này không hợp lệ để phát trực tuyến đến thiết bị này."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Không thể phát video này."</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 251389b73a08..e21ebcfe63b5 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -993,6 +993,18 @@ <string name="weeks" msgid="6509623834583944518">"周"</string> <string name="year" msgid="4001118221013892076">"年"</string> <string name="years" msgid="6881577717993213522">"年"</string> + <plurals name="duration_seconds"> + <item quantity="one" msgid="6962015528372969481">"1 秒"</item> + <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> 秒"</item> + </plurals> + <plurals name="duration_minutes"> + <item quantity="one" msgid="4915414002546085617">"1 分钟"</item> + <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> 分钟"</item> + </plurals> + <plurals name="duration_hours"> + <item quantity="one" msgid="8917467491248809972">"1 小时"</item> + <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> 小时"</item> + </plurals> <string name="VideoView_error_title" msgid="3534509135438353077">"视频问题"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"抱歉,该视频不适合在此设备上播放。"</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"无法播放此视频。"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 473b9d0b589e..a1286f1600f5 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -993,6 +993,12 @@ <string name="weeks" msgid="6509623834583944518">"週"</string> <string name="year" msgid="4001118221013892076">"年"</string> <string name="years" msgid="6881577717993213522">"年"</string> + <!-- no translation found for duration_seconds:one (6962015528372969481) --> + <!-- no translation found for duration_seconds:other (1886107766577166786) --> + <!-- no translation found for duration_minutes:one (4915414002546085617) --> + <!-- no translation found for duration_minutes:other (3165187169224908775) --> + <!-- no translation found for duration_hours:one (8917467491248809972) --> + <!-- no translation found for duration_hours:other (3863962854246773930) --> <string name="VideoView_error_title" msgid="3534509135438353077">"影片發生問題"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"這部影片的格式無效,因此無法在此裝置中串流播放。"</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"無法播放這部影片。"</string> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index eb1cbfb99360..27660deabff0 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -993,6 +993,12 @@ <string name="weeks" msgid="6509623834583944518">"amaviki"</string> <string name="year" msgid="4001118221013892076">"unyaka"</string> <string name="years" msgid="6881577717993213522">"iminyaka"</string> + <!-- no translation found for duration_seconds:one (6962015528372969481) --> + <!-- no translation found for duration_seconds:other (1886107766577166786) --> + <!-- no translation found for duration_minutes:one (4915414002546085617) --> + <!-- no translation found for duration_minutes:other (3165187169224908775) --> + <!-- no translation found for duration_hours:one (8917467491248809972) --> + <!-- no translation found for duration_hours:other (3863962854246773930) --> <string name="VideoView_error_title" msgid="3534509135438353077">"Inkinga yevidiyo"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Uxolo, le vidiyo ayilungele ukusakaza bukhomo kwale divaysi."</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Iyehluleka ukudlala levidiyo."</string> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 447daab8cece..48ee42957593 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -2107,8 +2107,8 @@ <enum name="locale" value="3" /> </attr> - <!-- Direction of the text. A heuristic is used to determine the resolved text direction - of paragraphs. --> + <!-- Defines the direction of the text. A heuristic is used to determine the resolved text + direction of paragraphs. --> <attr name="textDirection" format="integer"> <!-- Default --> <enum name="inherit" value="0" /> @@ -2128,7 +2128,7 @@ <enum name="locale" value="5" /> </attr> - <!-- Alignment of the text. A heuristic is used to determine the resolved + <!-- Defines the alignment of the text. A heuristic is used to determine the resolved text alignment. --> <attr name="textAlignment" format="integer"> <!-- Default --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index f91df99ab6d1..ea28a51fe547 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1006,8 +1006,8 @@ --> <integer-array name="config_defaultNotificationVibePattern"> <item>0</item> - <item>250</item> - <item>250</item> + <item>150</item> + <item>200</item> <item>250</item> </integer-array> @@ -1017,8 +1017,8 @@ --> <integer-array name="config_notificationFallbackVibePattern"> <item>0</item> - <item>250</item> - <item>250</item> - <item>250</item> + <item>33</item> + <item>150</item> + <item>50</item> </integer-array> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 57b5e4944eff..d1a7703d7138 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2840,6 +2840,21 @@ <!-- Appened to express the value is this unit of time. --> <string name="years">years</string> + <!-- Phrase describing a time duration using seconds [CHAR LIMIT=16] --> + <plurals name="duration_seconds"> + <item quantity="one">1 second</item> + <item quantity="other"><xliff:g id="count">%d</xliff:g> seconds</item> + </plurals> + <!-- Phrase describing a time duration using minutes [CHAR LIMIT=16] --> + <plurals name="duration_minutes"> + <item quantity="one">1 minute</item> + <item quantity="other"><xliff:g id="count">%d</xliff:g> minutes</item> + </plurals> + <!-- Phrase describing a time duration using hours [CHAR LIMIT=16] --> + <plurals name="duration_hours"> + <item quantity="one">1 hour</item> + <item quantity="other"><xliff:g id="count">%d</xliff:g> hours</item> + </plurals> <!-- Title for error alert when a video cannot be played. it can be used by any app. --> <string name="VideoView_error_title">Video problem</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 68587321b207..1d29d8ce2b35 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -870,6 +870,9 @@ <java-symbol type="plurals" name="abbrev_num_hours_ago" /> <java-symbol type="plurals" name="abbrev_num_minutes_ago" /> <java-symbol type="plurals" name="abbrev_num_seconds_ago" /> + <java-symbol type="plurals" name="duration_hours" /> + <java-symbol type="plurals" name="duration_minutes" /> + <java-symbol type="plurals" name="duration_seconds" /> <java-symbol type="plurals" name="in_num_days" /> <java-symbol type="plurals" name="in_num_hours" /> <java-symbol type="plurals" name="in_num_minutes" /> diff --git a/core/tests/coretests/src/android/text/format/DateUtilsTest.java b/core/tests/coretests/src/android/text/format/DateUtilsTest.java new file mode 100644 index 000000000000..cf42bb1c003a --- /dev/null +++ b/core/tests/coretests/src/android/text/format/DateUtilsTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.text.format; + +import android.test.suitebuilder.annotation.SmallTest; + +import junit.framework.TestCase; + +public class DateUtilsTest extends TestCase { + @SmallTest + public void testFormatDurationSeconds() throws Exception { + assertEquals("0 seconds", DateUtils.formatDuration(0)); + assertEquals("0 seconds", DateUtils.formatDuration(1)); + assertEquals("0 seconds", DateUtils.formatDuration(499)); + assertEquals("1 second", DateUtils.formatDuration(500)); + assertEquals("1 second", DateUtils.formatDuration(1000)); + assertEquals("2 seconds", DateUtils.formatDuration(1500)); + } + + @SmallTest + public void testFormatDurationMinutes() throws Exception { + assertEquals("59 seconds", DateUtils.formatDuration(59000)); + assertEquals("60 seconds", DateUtils.formatDuration(59500)); + assertEquals("1 minute", DateUtils.formatDuration(60000)); + assertEquals("2 minutes", DateUtils.formatDuration(120000)); + } + + @SmallTest + public void testFormatDurationHours() throws Exception { + assertEquals("59 minutes", DateUtils.formatDuration(3540000)); + assertEquals("1 hour", DateUtils.formatDuration(3600000)); + assertEquals("48 hours", DateUtils.formatDuration(172800000)); + } +} diff --git a/data/etc/platform.xml b/data/etc/platform.xml index 13d1791bad3e..83ecdd95b384 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -134,6 +134,7 @@ <assign-permission name="android.permission.ACCESS_NETWORK_STATE" uid="shell" /> <assign-permission name="android.permission.ACCESS_WIFI_STATE" uid="shell" /> <assign-permission name="android.permission.BLUETOOTH" uid="shell" /> + <assign-permission name="android.permission.EXPAND_STATUS_BAR" uid="shell" /> <!-- System tool permissions granted to the shell. --> <assign-permission name="android.permission.GET_TASKS" uid="shell" /> <assign-permission name="android.permission.CHANGE_CONFIGURATION" uid="shell" /> diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java index b4f1e84d6ba1..f6b5ffc9ad7c 100644 --- a/graphics/java/android/graphics/Path.java +++ b/graphics/java/android/graphics/Path.java @@ -59,6 +59,10 @@ public class Path { int valNative = 0; if (src != null) { valNative = src.mNativePath; + isSimplePath = src.isSimplePath; + if (src.rects != null) { + rects = new Region(src.rects); + } } mNativePath = init2(valNative); mDetectSimplePaths = HardwareRenderer.isAvailable(); @@ -544,6 +548,7 @@ public class Path { int dstNative = 0; if (dst != null) { dstNative = dst.mNativePath; + dst.isSimplePath = false; } native_offset(mNativePath, dx, dy, dstNative); } @@ -555,6 +560,7 @@ public class Path { * @param dy The amount in the Y direction to offset the entire path */ public void offset(float dx, float dy) { + isSimplePath = false; native_offset(mNativePath, dx, dy); } @@ -580,6 +586,7 @@ public class Path { public void transform(Matrix matrix, Path dst) { int dstNative = 0; if (dst != null) { + dst.isSimplePath = false; dstNative = dst.mNativePath; } native_transform(mNativePath, matrix.native_instance, dstNative); @@ -591,6 +598,7 @@ public class Path { * @param matrix The matrix to apply to the path */ public void transform(Matrix matrix) { + isSimplePath = false; native_transform(mNativePath, matrix.native_instance); } diff --git a/graphics/java/android/graphics/Point.java b/graphics/java/android/graphics/Point.java index 338e880abaf3..e0d8ccce0ec1 100644 --- a/graphics/java/android/graphics/Point.java +++ b/graphics/java/android/graphics/Point.java @@ -70,20 +70,29 @@ public class Point implements Parcelable { return this.x == x && this.y == y; } - @Override public boolean equals(Object o) { - if (o instanceof Point) { - Point p = (Point) o; - return this.x == p.x && this.y == p.y; - } - return false; + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Point point = (Point) o; + + if (x != point.x) return false; + if (y != point.y) return false; + + return true; } - @Override public int hashCode() { - return x * 32713 + y; + @Override + public int hashCode() { + int result = x; + result = 31 * result + y; + return result; } - @Override public String toString() { - return "Point(" + x + ", " + y+ ")"; + @Override + public String toString() { + return "Point(" + x + ", " + y + ")"; } /** diff --git a/graphics/java/android/graphics/PointF.java b/graphics/java/android/graphics/PointF.java index e00271f2e9a2..ee38dbb27d34 100644 --- a/graphics/java/android/graphics/PointF.java +++ b/graphics/java/android/graphics/PointF.java @@ -73,6 +73,31 @@ public class PointF implements Parcelable { return this.x == x && this.y == y; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + PointF pointF = (PointF) o; + + if (Float.compare(pointF.x, x) != 0) return false; + if (Float.compare(pointF.y, y) != 0) return false; + + return true; + } + + @Override + public int hashCode() { + int result = (x != +0.0f ? Float.floatToIntBits(x) : 0); + result = 31 * result + (y != +0.0f ? Float.floatToIntBits(y) : 0); + return result; + } + + @Override + public String toString() { + return "PointF(" + x + ", " + y + ")"; + } + /** * Return the euclidian distance from (0,0) to the point */ diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java index 41b272df53fc..8280d2d955a7 100644 --- a/graphics/java/android/graphics/drawable/DrawableContainer.java +++ b/graphics/java/android/graphics/drawable/DrawableContainer.java @@ -491,6 +491,8 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { mComputedConstantSize = orig.mComputedConstantSize; mConstantWidth = orig.mConstantWidth; mConstantHeight = orig.mConstantHeight; + mConstantMinimumWidth = orig.mConstantMinimumWidth; + mConstantMinimumHeight = orig.mConstantMinimumHeight; mOpacity = orig.mOpacity; mHaveStateful = orig.mHaveStateful; diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java index 0623a9ea0593..b966bb461135 100644 --- a/graphics/java/android/graphics/drawable/GradientDrawable.java +++ b/graphics/java/android/graphics/drawable/GradientDrawable.java @@ -479,7 +479,7 @@ public class GradientDrawable extends Drawable { mFillPaint.setDither(mDither); mFillPaint.setColorFilter(mColorFilter); if (mColorFilter != null && !mGradientState.mHasSolidColor) { - mFillPaint.setColor(0xff000000); + mFillPaint.setColor(mAlpha << 24); } if (haveStroke) { mStrokePaint.setAlpha(currStrokeAlpha); @@ -743,7 +743,7 @@ public class GradientDrawable extends Drawable { mFillPaint.setShader(new LinearGradient(x0, y0, x1, y1, colors, st.mPositions, Shader.TileMode.CLAMP)); if (!mGradientState.mHasSolidColor) { - mFillPaint.setColor(0xff000000); + mFillPaint.setColor(mAlpha << 24); } } else if (st.mGradient == RADIAL_GRADIENT) { x0 = r.left + (r.right - r.left) * st.mCenterX; @@ -755,7 +755,7 @@ public class GradientDrawable extends Drawable { level * st.mGradientRadius, colors, null, Shader.TileMode.CLAMP)); if (!mGradientState.mHasSolidColor) { - mFillPaint.setColor(0xff000000); + mFillPaint.setColor(mAlpha << 24); } } else if (st.mGradient == SWEEP_GRADIENT) { x0 = r.left + (r.right - r.left) * st.mCenterX; @@ -788,7 +788,7 @@ public class GradientDrawable extends Drawable { } mFillPaint.setShader(new SweepGradient(x0, y0, tempColors, tempPositions)); if (!mGradientState.mHasSolidColor) { - mFillPaint.setColor(0xff000000); + mFillPaint.setColor(mAlpha << 24); } } } diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 22f699ff0b8f..56abed4d416e 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -5088,18 +5088,23 @@ public class AudioService extends IAudioService.Stub implements OnFinished { // top of the stack for the media button event receivers : simply using the top of the // stack would make the entry disappear from the RemoteControlDisplay in conditions such as // notifications playing during music playback. - // crawl the AudioFocus stack until an entry is found with the following characteristics: + // Crawl the AudioFocus stack from the top until an entry is found with the following + // characteristics: // - focus gain on STREAM_MUSIC stream // - non-transient focus gain on a stream other than music FocusStackEntry af = null; - Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator(); - while(stackIterator.hasNext()) { - FocusStackEntry fse = (FocusStackEntry)stackIterator.next(); - if ((fse.mStreamType == AudioManager.STREAM_MUSIC) - || (fse.mFocusChangeType == AudioManager.AUDIOFOCUS_GAIN)) { - af = fse; - break; + try { + for (int index = mFocusStack.size()-1; index >= 0; index--) { + FocusStackEntry fse = mFocusStack.elementAt(index); + if ((fse.mStreamType == AudioManager.STREAM_MUSIC) + || (fse.mFocusChangeType == AudioManager.AUDIOFOCUS_GAIN)) { + af = fse; + break; + } } + } catch (ArrayIndexOutOfBoundsException e) { + Log.e(TAG, "Wrong index accessing audio focus stack when updating RCD: " + e); + af = null; } if (af == null) { clearRemoteControlDisplay_syncAfRcs(); @@ -5120,6 +5125,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { clearRemoteControlDisplay_syncAfRcs(); return; } + // refresh conditions were verified: update the remote controls // ok to call: synchronized mAudioFocusLock then on mRCStack, mRCStack is not empty updateRemoteControlDisplay_syncAfRcs(infoChangedFlags); diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index 4414191be531..169502b68162 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -50,7 +50,7 @@ import java.util.Map; * <tr><th>Name</th><th>Value Type</th><th>Description</th></tr> * <tr><td>{@link #KEY_CHANNEL_COUNT}</td><td>Integer</td><td></td></tr> * <tr><td>{@link #KEY_SAMPLE_RATE}</td><td>Integer</td><td></td></tr> - * <tr><td>{@link #KEY_IS_ADTS}</td><td>Integer</td><td>optional, if content is AAC audio, setting this key to 1 indicates that each audio frame is prefixed by the ADTS header.</td></tr> + * <tr><td>{@link #KEY_IS_ADTS}</td><td>Integer</td><td>optional, if <em>decoding</em> AAC audio content, setting this key to 1 indicates that each audio frame is prefixed by the ADTS header.</td></tr> * <tr><td>{@link #KEY_AAC_PROFILE}</td><td>Integer</td><td><b>encoder-only</b>, optional, if content is AAC audio, specifies the desired profile.</td></tr> * <tr><td>{@link #KEY_CHANNEL_MASK}</td><td>Integer</td><td>A mask of audio channel assignments</td></tr> * <tr><td>{@link #KEY_FLAC_COMPRESSION_LEVEL}</td><td>Integer</td><td><b>encoder-only</b>, optional, if content is FLAC audio, specifies the desired compression level.</td></tr> @@ -140,6 +140,8 @@ public final class MediaFormat { * A key mapping to a value of 1 if the content is AAC audio and * audio frames are prefixed with an ADTS header. * The associated value is an integer (0 or 1). + * This key is only supported when _decoding_ content, it cannot + * be used to configure an encoder to emit ADTS output. */ public static final String KEY_IS_ADTS = "is-adts"; diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java index 2a5a16e60a61..8701f3662605 100644 --- a/media/java/android/media/MediaRouter.java +++ b/media/java/android/media/MediaRouter.java @@ -862,7 +862,7 @@ public class MediaRouter { private static WifiDisplay findMatchingDisplay(WifiDisplay d, WifiDisplay[] displays) { for (int i = 0; i < displays.length; i++) { final WifiDisplay other = displays[i]; - if (d.getDeviceAddress().equals(other.getDeviceAddress())) { + if (d.hasSameAddress(other)) { return other; } } diff --git a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml index 2669c7ecff1f..b1104ccd70ae 100644 --- a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml +++ b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml @@ -141,7 +141,7 @@ /> </LinearLayout> - <ImageView + <com.android.systemui.statusbar.policy.KeyButtonView android:layout_width="128dp" android:id="@+id/search_light" android:layout_height="match_parent" @@ -282,7 +282,7 @@ /> </LinearLayout> - <ImageView + <com.android.systemui.statusbar.policy.KeyButtonView android:layout_width="162dp" android:id="@+id/search_light" android:layout_height="match_parent" diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml index 440a4e1f1c86..da52d89cb287 100644 --- a/packages/SystemUI/res/layout/navigation_bar.xml +++ b/packages/SystemUI/res/layout/navigation_bar.xml @@ -145,7 +145,7 @@ /> </LinearLayout> - <ImageView + <com.android.systemui.statusbar.policy.KeyButtonView android:layout_width="80dp" android:id="@+id/search_light" android:layout_height="match_parent" @@ -289,7 +289,7 @@ /> </LinearLayout> - <ImageView + <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/search_light" android:layout_height="80dp" android:layout_width="match_parent" diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java index 6cb7decdbfd0..33303017355e 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java @@ -336,19 +336,6 @@ public class RecentsHorizontalScrollView extends HorizontalScrollView }); } - @Override - protected void onVisibilityChanged(View changedView, int visibility) { - super.onVisibilityChanged(changedView, visibility); - // scroll to bottom after reloading - if (visibility == View.VISIBLE && changedView == this) { - post(new Runnable() { - public void run() { - update(); - } - }); - } - } - public void setAdapter(TaskDescriptionAdapter adapter) { mAdapter = adapter; mAdapter.registerDataSetObserver(new DataSetObserver() { diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java index cd3bc4250000..76cd33fd3095 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java @@ -77,9 +77,10 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener private boolean mShowing; private boolean mWaitingToShow; - private int mNumItemsWaitingForThumbnailsAndIcons; private ViewHolder mItemToAnimateInWhenWindowAnimationIsFinished; + private boolean mAnimateIconOfFirstTask; private boolean mWaitingForWindowAnimation; + private long mWindowAnimationStartTime; private RecentTasksLoader mRecentTasksLoader; private ArrayList<TaskDescription> mRecentTaskDescriptions; @@ -174,10 +175,9 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener if (td.isLoaded()) { updateThumbnail(holder, td.getThumbnail(), true, false); updateIcon(holder, td.getIcon(), true, false); - mNumItemsWaitingForThumbnailsAndIcons--; } if (index == 0) { - if (mWaitingForWindowAnimation) { + if (mAnimateIconOfFirstTask) { if (mItemToAnimateInWhenWindowAnimationIsFinished != null) { holder.iconView.setAlpha(1f); holder.iconView.setTranslationX(0f); @@ -206,6 +206,9 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener holder.iconView.setAlpha(0f); holder.iconView.setTranslationY(translation); } + if (!mWaitingForWindowAnimation) { + animateInIconOfFirstTask(); + } } } @@ -220,7 +223,9 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener updateThumbnail(holder, mRecentTasksLoader.getDefaultThumbnail(), false, false); holder.iconView.setImageBitmap(mRecentTasksLoader.getDefaultIcon()); holder.iconView.setVisibility(INVISIBLE); + holder.iconView.animate().cancel(); holder.labelView.setText(null); + holder.labelView.animate().cancel(); holder.thumbnailView.setContentDescription(null); holder.thumbnailView.setTag(null); holder.thumbnailView.setOnLongClickListener(null); @@ -235,6 +240,7 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener holder.calloutLine.setAlpha(1f); holder.calloutLine.setTranslationX(0f); holder.calloutLine.setTranslationY(0f); + holder.calloutLine.animate().cancel(); } holder.taskDescription = null; holder.loadedThumbnailAndIcon = false; @@ -291,8 +297,9 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener } public void show(boolean show, ArrayList<TaskDescription> recentTaskDescriptions, - boolean firstScreenful, boolean waitingForWindowAnimation) { - mWaitingForWindowAnimation = waitingForWindowAnimation; + boolean firstScreenful, boolean animateIconOfFirstTask) { + mAnimateIconOfFirstTask = animateIconOfFirstTask; + mWaitingForWindowAnimation = animateIconOfFirstTask; if (show) { mWaitingToShow = true; refreshRecentTasksList(recentTaskDescriptions, firstScreenful); @@ -527,7 +534,6 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener updateIcon(h, td.getIcon(), true, animateShow); updateThumbnail(h, td.getThumbnail(), true, animateShow); h.loadedThumbnailAndIcon = true; - mNumItemsWaitingForThumbnailsAndIcons--; } } } @@ -536,9 +542,14 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener showIfReady(); } - public void onWindowAnimationStart() { - if (mItemToAnimateInWhenWindowAnimationIsFinished != null) { - final int startDelay = 150; + private void animateInIconOfFirstTask() { + if (mItemToAnimateInWhenWindowAnimationIsFinished != null && + !mRecentTasksLoader.isFirstScreenful()) { + int timeSinceWindowAnimation = + (int) (System.currentTimeMillis() - mWindowAnimationStartTime); + final int minStartDelay = 150; + final int startDelay = Math.max(0, Math.min( + minStartDelay - timeSinceWindowAnimation, minStartDelay)); final int duration = 250; final ViewHolder holder = mItemToAnimateInWhenWindowAnimationIsFinished; final TimeInterpolator cubic = new DecelerateInterpolator(1.5f); @@ -550,10 +561,16 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener } } mItemToAnimateInWhenWindowAnimationIsFinished = null; - mWaitingForWindowAnimation = false; + mAnimateIconOfFirstTask = false; } } + public void onWindowAnimationStart() { + mWaitingForWindowAnimation = false; + mWindowAnimationStartTime = System.currentTimeMillis(); + animateInIconOfFirstTask(); + } + public void clearRecentTasksList() { // Clear memory used by screenshots if (mRecentTaskDescriptions != null) { @@ -590,9 +607,6 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener } public void onTasksLoaded(ArrayList<TaskDescription> tasks, boolean firstScreenful) { - mNumItemsWaitingForThumbnailsAndIcons = firstScreenful - ? tasks.size() : mRecentTaskDescriptions == null - ? 0 : mRecentTaskDescriptions.size(); if (mRecentTaskDescriptions == null) { mRecentTaskDescriptions = new ArrayList<TaskDescription>(tasks); } else { diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java index 47b01134f10a..b3adbaf5d131 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java @@ -345,19 +345,6 @@ public class RecentsVerticalScrollView extends ScrollView }); } - @Override - protected void onVisibilityChanged(View changedView, int visibility) { - super.onVisibilityChanged(changedView, visibility); - // scroll to bottom after reloading - if (visibility == View.VISIBLE && changedView == this) { - post(new Runnable() { - public void run() { - update(); - } - }); - } - } - public void setAdapter(TaskDescriptionAdapter adapter) { mAdapter = adapter; mAdapter.registerDataSetObserver(new DataSetObserver() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java index cc9c6018dc79..9b0a3207113e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java @@ -55,6 +55,7 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.LevelListDrawable; import android.hardware.display.DisplayManager; import android.hardware.display.WifiDisplayStatus; +import android.net.wifi.WifiManager; import android.os.AsyncTask; import android.os.Handler; import android.os.RemoteException; @@ -85,6 +86,8 @@ class QuickSettings { private static final String TAG = "QuickSettings"; public static final boolean SHOW_IME_TILE = false; + public static final boolean LONG_PRESS_TOGGLES = true; + private Context mContext; private PanelBar mBar; private QuickSettingsModel mModel; @@ -94,6 +97,8 @@ class QuickSettings { private WifiDisplayStatus mWifiDisplayStatus; private PhoneStatusBar mStatusBarService; private BluetoothState mBluetoothState; + private BluetoothAdapter mBluetoothAdapter; + private WifiManager mWifiManager; private BrightnessController mBrightnessController; private BluetoothController mBluetoothController; @@ -131,6 +136,9 @@ class QuickSettings { mModel = new QuickSettingsModel(context); mWifiDisplayStatus = new WifiDisplayStatus(); mBluetoothState = new QuickSettingsModel.BluetoothState(); + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + mHandler = new Handler(); Resources r = mContext.getResources(); @@ -297,8 +305,7 @@ class QuickSettings { (UserManager) mContext.getSystemService(Context.USER_SERVICE); if (um.getUsers(true).size() > 1) { try { - WindowManagerGlobal.getWindowManagerService().lockNow( - LockPatternUtils.USER_SWITCH_LOCK_OPTIONS); + WindowManagerGlobal.getWindowManagerService().lockNow(null); } catch (RemoteException e) { Log.e(TAG, "Couldn't show user switcher", e); } @@ -391,7 +398,7 @@ class QuickSettings { private void addSystemTiles(ViewGroup parent, LayoutInflater inflater) { // Wi-fi - QuickSettingsTileView wifiTile = (QuickSettingsTileView) + final QuickSettingsTileView wifiTile = (QuickSettingsTileView) inflater.inflate(R.layout.quick_settings_tile, parent, false); wifiTile.setContent(R.layout.quick_settings_tile_wifi, inflater); wifiTile.setOnClickListener(new View.OnClickListener() { @@ -400,6 +407,30 @@ class QuickSettings { startSettingsActivity(android.provider.Settings.ACTION_WIFI_SETTINGS); } }); + if (LONG_PRESS_TOGGLES) { + wifiTile.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + final boolean enable = + (mWifiManager.getWifiState() != WifiManager.WIFI_STATE_ENABLED); + new AsyncTask<Void, Void, Void>() { + @Override + protected Void doInBackground(Void... args) { + // Disable tethering if enabling Wifi + final int wifiApState = mWifiManager.getWifiApState(); + if (enable && ((wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) || + (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) { + mWifiManager.setWifiApEnabled(null, false); + } + + mWifiManager.setWifiEnabled(enable); + return null; + } + }.execute(); + wifiTile.setPressed(false); + return true; + }} ); + } mModel.addWifiTile(wifiTile, new QuickSettingsModel.RefreshCallback() { @Override public void refreshView(QuickSettingsTileView view, State state) { @@ -415,7 +446,7 @@ class QuickSettings { }); parent.addView(wifiTile); - if (mModel.deviceSupportsTelephony()) { + if (mModel.deviceHasMobileData()) { // RSSI QuickSettingsTileView rssiTile = (QuickSettingsTileView) inflater.inflate(R.layout.quick_settings_tile, parent, false); @@ -538,7 +569,7 @@ class QuickSettings { // Bluetooth if (mModel.deviceSupportsBluetooth()) { - QuickSettingsTileView bluetoothTile = (QuickSettingsTileView) + final QuickSettingsTileView bluetoothTile = (QuickSettingsTileView) inflater.inflate(R.layout.quick_settings_tile, parent, false); bluetoothTile.setContent(R.layout.quick_settings_tile_bluetooth, inflater); bluetoothTile.setOnClickListener(new View.OnClickListener() { @@ -547,6 +578,19 @@ class QuickSettings { startSettingsActivity(android.provider.Settings.ACTION_BLUETOOTH_SETTINGS); } }); + if (LONG_PRESS_TOGGLES) { + bluetoothTile.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + if (mBluetoothAdapter.isEnabled()) { + mBluetoothAdapter.disable(); + } else { + mBluetoothAdapter.enable(); + } + bluetoothTile.setPressed(false); + return true; + }}); + } mModel.addBluetoothTile(bluetoothTile, new QuickSettingsModel.RefreshCallback() { @Override public void refreshView(QuickSettingsTileView view, State state) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java index 4513dcb8060f..ec428834706e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java @@ -29,6 +29,7 @@ import android.content.res.Resources; import android.database.ContentObserver; import android.graphics.drawable.Drawable; import android.hardware.display.WifiDisplayStatus; +import android.net.ConnectivityManager; import android.os.Handler; import android.os.UserHandle; import android.provider.Settings; @@ -171,6 +172,8 @@ class QuickSettingsModel implements BluetoothStateChangeCallback, private final BugreportObserver mBugreportObserver; private final BrightnessObserver mBrightnessObserver; + private final boolean mHasMobileData; + private QuickSettingsTileView mUserTile; private RefreshCallback mUserCallback; private UserState mUserState = new UserState(); @@ -249,6 +252,10 @@ class QuickSettingsModel implements BluetoothStateChangeCallback, mBrightnessObserver = new BrightnessObserver(mHandler); mBrightnessObserver.startObserving(); + ConnectivityManager cm = (ConnectivityManager) + context.getSystemService(Context.CONNECTIVITY_SERVICE); + mHasMobileData = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE); + IntentFilter alarmIntentFilter = new IntentFilter(); alarmIntentFilter.addAction(Intent.ACTION_ALARM_CHANGED); context.registerReceiver(mAlarmIntentReceiver, alarmIntentFilter); @@ -403,22 +410,22 @@ class QuickSettingsModel implements BluetoothStateChangeCallback, mWifiCallback.refreshView(mWifiTile, mWifiState); } + boolean deviceHasMobileData() { + return mHasMobileData; + } + // RSSI void addRSSITile(QuickSettingsTileView view, RefreshCallback cb) { mRSSITile = view; mRSSICallback = cb; mRSSICallback.refreshView(mRSSITile, mRSSIState); } - boolean deviceSupportsTelephony() { - PackageManager pm = mContext.getPackageManager(); - return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY); - } // NetworkSignalChanged callback @Override public void onMobileDataSignalChanged( boolean enabled, int mobileSignalIconId, String signalContentDescription, int dataTypeIconId, String dataContentDescription, String enabledDesc) { - if (deviceSupportsTelephony()) { + if (deviceHasMobileData()) { // TODO: If view is in awaiting state, disable Resources r = mContext.getResources(); mRSSIState.signalIconId = enabled && (mobileSignalIconId > 0) diff --git a/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java b/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java index dbd999913637..762711d1b745 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java @@ -18,10 +18,9 @@ package com.android.internal.policy.impl.keyguard; import android.content.Context; import android.content.pm.PackageManager.NameNotFoundException; -import android.graphics.Bitmap; -import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Point; +import android.graphics.Rect; import android.os.Handler; import android.os.SystemClock; import android.util.Log; @@ -53,17 +52,20 @@ public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnCli private final Handler mHandler = new Handler(); private final KeyguardActivityLauncher mActivityLauncher; private final Callbacks mCallbacks; + private final CameraWidgetInfo mWidgetInfo; private final WindowManager mWindowManager; private final Point mRenderedSize = new Point(); - private final int[] mScreenLocation = new int[2]; + private final int[] mTmpLoc = new int[2]; + private final Rect mTmpRect = new Rect(); - private View mWidgetView; private long mLaunchCameraStart; private boolean mActive; private boolean mTransitioning; - private boolean mRecovering; private boolean mDown; + private FixedSizeFrameLayout mPreview; + private View mFullscreenPreview; + private final Runnable mTransitionToCameraRunnable = new Runnable() { @Override public void run() { @@ -81,19 +83,16 @@ public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnCli mActivityLauncher.launchCamera(worker, mSecureCameraActivityStartedRunnable); }}; - private final Runnable mRecoverRunnable = new Runnable() { + private final Runnable mPostTransitionToCameraEndAction = new Runnable() { @Override public void run() { - recover(); + mHandler.post(mTransitionToCameraEndAction); }}; - private final Runnable mRecoverEndAction = new Runnable() { + private final Runnable mRecoverRunnable = new Runnable() { @Override public void run() { - if (!mRecovering) - return; - mCallbacks.onCameraLaunchedUnsuccessfully(); - reset(); + recover(); }}; private final Runnable mRenderRunnable = new Runnable() { @@ -119,13 +118,43 @@ public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnCli }; }; + private static final class FixedSizeFrameLayout extends FrameLayout { + int width; + int height; + + FixedSizeFrameLayout(Context context) { + super(context); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + measureChildren( + MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); + setMeasuredDimension(width, height); + } + } + private CameraWidgetFrame(Context context, Callbacks callbacks, - KeyguardActivityLauncher activityLauncher) { + KeyguardActivityLauncher activityLauncher, + CameraWidgetInfo widgetInfo, View previewWidget) { super(context); mCallbacks = callbacks; mActivityLauncher = activityLauncher; + mWidgetInfo = widgetInfo; mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); KeyguardUpdateMonitor.getInstance(context).registerCallback(mCallback); + + mPreview = new FixedSizeFrameLayout(context); + mPreview.addView(previewWidget); + addView(mPreview); + + View clickBlocker = new View(context); + clickBlocker.setBackgroundColor(Color.TRANSPARENT); + clickBlocker.setOnClickListener(this); + addView(clickBlocker); + + setContentDescription(context.getString(R.string.keyguard_accessibility_camera)); if (DEBUG) Log.d(TAG, "new CameraWidgetFrame instance " + instanceId()); } @@ -137,24 +166,17 @@ public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnCli CameraWidgetInfo widgetInfo = launcher.getCameraWidgetInfo(); if (widgetInfo == null) return null; - View widgetView = widgetInfo.layoutId > 0 ? - inflateWidgetView(context, widgetInfo) : - inflateGenericWidgetView(context); - if (widgetView == null) + View previewWidget = getPreviewWidget(context, widgetInfo); + if (previewWidget == null) return null; - ImageView preview = new ImageView(context); - preview.setLayoutParams(new FrameLayout.LayoutParams( - FrameLayout.LayoutParams.MATCH_PARENT, - FrameLayout.LayoutParams.MATCH_PARENT)); - preview.setScaleType(ScaleType.FIT_CENTER); - preview.setContentDescription(preview.getContext().getString( - R.string.keyguard_accessibility_camera)); - CameraWidgetFrame cameraWidgetFrame = new CameraWidgetFrame(context, callbacks, launcher); - cameraWidgetFrame.addView(preview); - cameraWidgetFrame.mWidgetView = widgetView; - preview.setOnClickListener(cameraWidgetFrame); - return cameraWidgetFrame; + return new CameraWidgetFrame(context, callbacks, launcher, widgetInfo, previewWidget); + } + + private static View getPreviewWidget(Context context, CameraWidgetInfo widgetInfo) { + return widgetInfo.layoutId > 0 ? + inflateWidgetView(context, widgetInfo) : + inflateGenericWidgetView(context); } private static View inflateWidgetView(Context context, CameraWidgetInfo widgetInfo) { @@ -188,119 +210,99 @@ public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnCli return iv; } - public void render() { - final Throwable[] thrown = new Throwable[1]; - final Bitmap[] offscreen = new Bitmap[1]; - try { - final int width = getRootView().getWidth(); - final int height = getRootView().getHeight(); - if (mRenderedSize.x == width && mRenderedSize.y == height) { - if (DEBUG) Log.d(TAG, String.format("Already rendered at size=%sx%s", - width, height)); - return; - } - if (width == 0 || height == 0) { - return; - } - final long start = SystemClock.uptimeMillis(); - offscreen[0] = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - final Canvas c = new Canvas(offscreen[0]); - mWidgetView.measure( - MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); - mWidgetView.layout(0, 0, width, height); - mWidgetView.draw(c); - - final long end = SystemClock.uptimeMillis(); - if (DEBUG) Log.d(TAG, String.format( - "Rendered camera widget in %sms size=%sx%s instance=%s at %s", - end - start, - width, height, - instanceId(), - end)); - mRenderedSize.set(width, height); - } catch (Throwable t) { - thrown[0] = t; + private void render() { + final View root = getRootView(); + final int width = root.getWidth(); + final int height = root.getHeight(); + if (mRenderedSize.x == width && mRenderedSize.y == height) { + if (DEBUG) Log.d(TAG, String.format("Already rendered at size=%sx%s", width, height)); + return; + } + if (width == 0 || height == 0) { + return; } - mHandler.post(new Runnable() { - @Override - public void run() { - if (thrown[0] == null) { - try { - ((ImageView) getChildAt(0)).setImageBitmap(offscreen[0]); - } catch (Throwable t) { - thrown[0] = t; - } - } - if (thrown[0] == null) - return; - - Log.w(TAG, "Error rendering camera widget", thrown[0]); - try { - removeAllViews(); - final View genericView = inflateGenericWidgetView(mContext); - addView(genericView); - } catch (Throwable t) { - Log.w(TAG, "Error inflating generic camera widget", t); - } - }}); - } - - private void transitionToCamera() { - if (mTransitioning || mDown) return; + mPreview.width = width; + mPreview.height = height; + mPreview.requestLayout(); - mTransitioning = true; + final int thisWidth = getWidth() - getPaddingLeft() - getPaddingRight(); + final int thisHeight = getHeight() - getPaddingTop() - getPaddingBottom(); - final View child = getChildAt(0); - final View root = getRootView(); + final float pvScaleX = (float) thisWidth / width; + final float pvScaleY = (float) thisHeight / height; + final float pvScale = Math.min(pvScaleX, pvScaleY); - final int startWidth = child.getWidth(); - final int startHeight = child.getHeight(); + final int pvWidth = (int) (pvScale * width); + final int pvHeight = (int) (pvScale * height); - final int finishWidth = root.getWidth(); - final int finishHeight = root.getHeight(); + final float pvTransX = pvWidth < thisWidth ? (thisWidth - pvWidth) / 2 : 0; + final float pvTransY = pvHeight < thisHeight ? (thisHeight - pvHeight) / 2 : 0; - final float scaleX = (float) finishWidth / startWidth; - final float scaleY = (float) finishHeight / startHeight; - final float scale = Math.round( Math.max(scaleX, scaleY) * 100) / 100f; + mPreview.setPivotX(0); + mPreview.setPivotY(0); + mPreview.setScaleX(pvScale); + mPreview.setScaleY(pvScale); + mPreview.setTranslationX(pvTransX); + mPreview.setTranslationY(pvTransY); - final int[] loc = new int[2]; - root.getLocationInWindow(loc); - final int finishCenter = loc[1] + finishHeight / 2; + mRenderedSize.set(width, height); + if (DEBUG) Log.d(TAG, String.format("Rendered camera widget size=%sx%s instance=%s", + width, height, instanceId())); + } - child.getLocationInWindow(loc); - final int startCenter = loc[1] + startHeight / 2; + private void transitionToCamera() { + if (mTransitioning || mDown) return; - if (DEBUG) Log.d(TAG, String.format("Transitioning to camera. " + - "(start=%sx%s, finish=%sx%s, scale=%s,%s, startCenter=%s, finishCenter=%s)", - startWidth, startHeight, - finishWidth, finishHeight, - scaleX, scaleY, - startCenter, finishCenter)); + mTransitioning = true; enableWindowExitAnimation(false); - animate() - .scaleX(scale) - .scaleY(scale) - .translationY(finishCenter - startCenter) - .setDuration(WIDGET_ANIMATION_DURATION) - .withEndAction(mTransitionToCameraEndAction) - .start(); - mCallbacks.onLaunchingCamera(); - } + mPreview.getLocationInWindow(mTmpLoc); + final float pvHeight = mPreview.getHeight() * mPreview.getScaleY(); + final float pvCenter = mTmpLoc[1] + pvHeight / 2f; - private void recover() { - if (DEBUG) Log.d(TAG, "recovering at " + SystemClock.uptimeMillis()); - mRecovering = true; - animate() + final ViewGroup root = (ViewGroup) getRootView(); + if (mFullscreenPreview == null) { + mFullscreenPreview = getPreviewWidget(mContext, mWidgetInfo); + mFullscreenPreview.setClickable(false); + root.addView(mFullscreenPreview); + } + + root.getWindowVisibleDisplayFrame(mTmpRect); + final float fsHeight = mTmpRect.height(); + final float fsCenter = mTmpRect.top + fsHeight / 2; + + final float fsScaleY = pvHeight / fsHeight; + final float fsTransY = pvCenter - fsCenter; + final float fsScaleX = mPreview.getScaleX(); + + mPreview.setVisibility(View.GONE); + mFullscreenPreview.setVisibility(View.VISIBLE); + mFullscreenPreview.setTranslationY(fsTransY); + mFullscreenPreview.setScaleX(fsScaleX); + mFullscreenPreview.setScaleY(fsScaleY); + mFullscreenPreview + .animate() .scaleX(1) .scaleY(1) + .translationX(0) .translationY(0) .setDuration(WIDGET_ANIMATION_DURATION) - .withEndAction(mRecoverEndAction) + .withEndAction(mPostTransitionToCameraEndAction) .start(); + mCallbacks.onLaunchingCamera(); + } + + private void recover() { + if (DEBUG) Log.d(TAG, "recovering at " + SystemClock.uptimeMillis()); + mCallbacks.onCameraLaunchedUnsuccessfully(); + reset(); + } + + @Override + public void setOnLongClickListener(OnLongClickListener l) { + // ignore } @Override @@ -340,8 +342,8 @@ public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnCli return true; } - getLocationOnScreen(mScreenLocation); - int rawBottom = mScreenLocation[1] + getHeight(); + getLocationOnScreen(mTmpLoc); + int rawBottom = mTmpLoc[1] + getHeight(); if (event.getRawY() > rawBottom) { if (DEBUG) Log.d(TAG, "onUserInteraction eaten: below widget"); return true; @@ -388,14 +390,14 @@ public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnCli if (DEBUG) Log.d(TAG, "reset at " + SystemClock.uptimeMillis()); mLaunchCameraStart = 0; mTransitioning = false; - mRecovering = false; mDown = false; cancelTransitionToCamera(); mHandler.removeCallbacks(mRecoverRunnable); - animate().cancel(); - setScaleX(1); - setScaleY(1); - setTranslationY(0); + mPreview.setVisibility(View.VISIBLE); + if (mFullscreenPreview != null) { + mFullscreenPreview.animate().cancel(); + mFullscreenPreview.setVisibility(View.GONE); + } enableWindowExitAnimation(true); } @@ -403,11 +405,18 @@ public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnCli protected void onSizeChanged(int w, int h, int oldw, int oldh) { if (DEBUG) Log.d(TAG, String.format("onSizeChanged new=%sx%s old=%sx%s at %s", w, h, oldw, oldh, SystemClock.uptimeMillis())); - final Handler worker = getWorkerHandler(); - (worker != null ? worker : mHandler).post(mRenderRunnable); + mHandler.post(mRenderRunnable); super.onSizeChanged(w, h, oldw, oldh); } + @Override + public void onBouncerShowing(boolean showing) { + if (showing) { + mTransitioning = false; + mHandler.post(mRecoverRunnable); + } + } + private void enableWindowExitAnimation(boolean isEnabled) { View root = getRootView(); ViewGroup.LayoutParams lp = root.getLayoutParams(); @@ -427,15 +436,14 @@ public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnCli if (DEBUG) Log.d(TAG, "onKeyguardVisibilityChanged " + showing + " at " + SystemClock.uptimeMillis()); if (mTransitioning && !showing) { - mTransitioning = false; - mRecovering = false; - mHandler.removeCallbacks(mRecoverRunnable); - if (mLaunchCameraStart > 0) { - long launchTime = SystemClock.uptimeMillis() - mLaunchCameraStart; - if (DEBUG) Log.d(TAG, String.format("Camera took %sms to launch", launchTime)); - mLaunchCameraStart = 0; - onCameraLaunched(); - } + mTransitioning = false; + mHandler.removeCallbacks(mRecoverRunnable); + if (mLaunchCameraStart > 0) { + long launchTime = SystemClock.uptimeMillis() - mLaunchCameraStart; + if (DEBUG) Log.d(TAG, String.format("Camera took %sms to launch", launchTime)); + mLaunchCameraStart = 0; + onCameraLaunched(); + } } } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java index de19bd57dcf6..11ed5bc8bdb4 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java @@ -26,7 +26,6 @@ import android.appwidget.AppWidgetHost; import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; -import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -82,6 +81,7 @@ public class KeyguardHostView extends KeyguardViewBase { private int mAppWidgetToShow; private boolean mCheckAppWidgetConsistencyOnBootCompleted = false; + private boolean mCleanupAppWidgetsOnBootCompleted = false; protected OnDismissAction mDismissAction; @@ -128,6 +128,8 @@ public class KeyguardHostView extends KeyguardViewBase { mLockPatternUtils = new LockPatternUtils(context); mAppWidgetHost = new AppWidgetHost( context, APPWIDGET_HOST_ID, mOnClickHandler, Looper.myLooper()); + cleanupAppWidgetIds(); + mAppWidgetManager = AppWidgetManager.getInstance(mContext); mSecurityModel = new KeyguardSecurityModel(context); @@ -153,6 +155,39 @@ public class KeyguardHostView extends KeyguardViewBase { } } + private void cleanupAppWidgetIds() { + // Since this method may delete a widget (which we can't do until boot completed) we + // may have to defer it until after boot complete. + if (!KeyguardUpdateMonitor.getInstance(mContext).hasBootCompleted()) { + mCleanupAppWidgetsOnBootCompleted = true; + return; + } + // Clean up appWidgetIds that are bound to lockscreen, but not actually used + // This is only to clean up after another bug: we used to not call + // deleteAppWidgetId when a user manually deleted a widget in keyguard. This code + // shouldn't have to run more than once per user. AppWidgetProviders rely on callbacks + // that are triggered by deleteAppWidgetId, which is why we're doing this + int[] appWidgetIdsInKeyguardSettings = mLockPatternUtils.getAppWidgets(); + int[] appWidgetIdsBoundToHost = mAppWidgetHost.getAppWidgetIds(); + for (int i = 0; i < appWidgetIdsBoundToHost.length; i++) { + int appWidgetId = appWidgetIdsBoundToHost[i]; + if (!contains(appWidgetIdsInKeyguardSettings, appWidgetId)) { + Log.d(TAG, "Found a appWidgetId that's not being used by keyguard, deleting id " + + appWidgetId); + mAppWidgetHost.deleteAppWidgetId(appWidgetId); + } + } + } + + private static boolean contains(int[] array, int target) { + for (int value : array) { + if (value == target) { + return true; + } + } + return false; + } + private KeyguardUpdateMonitorCallback mUpdateMonitorCallbacks = new KeyguardUpdateMonitorCallback() { @Override @@ -162,6 +197,10 @@ public class KeyguardHostView extends KeyguardViewBase { mSwitchPageRunnable.run(); mCheckAppWidgetConsistencyOnBootCompleted = false; } + if (mCleanupAppWidgetsOnBootCompleted) { + cleanupAppWidgetIds(); + mCleanupAppWidgetsOnBootCompleted = false; + } } }; @@ -232,7 +271,7 @@ public class KeyguardHostView extends KeyguardViewBase { addWidgetsFromSettings(); if (numWidgets() >= MAX_WIDGETS) { - setAddWidgetEnabled(false); + mAppWidgetContainer.setAddWidgetEnabled(false); } checkAppWidgetConsistency(); mSwitchPageRunnable.run(); @@ -326,14 +365,25 @@ public class KeyguardHostView extends KeyguardViewBase { @Override public void onAddView(View v) { if (numWidgets() >= MAX_WIDGETS) { - setAddWidgetEnabled(false); + mAppWidgetContainer.setAddWidgetEnabled(false); } - }; + } + + @Override + public void onRemoveView(View v, boolean deletePermanently) { + if (deletePermanently) { + final int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId(); + if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID && + appWidgetId != LockPatternUtils.ID_DEFAULT_STATUS_WIDGET) { + mAppWidgetHost.deleteAppWidgetId(appWidgetId); + } + } + } @Override - public void onRemoveView(View v) { + public void onRemoveViewAnimationCompleted() { if (numWidgets() < MAX_WIDGETS) { - setAddWidgetEnabled(true); + mAppWidgetContainer.setAddWidgetEnabled(true); } } }; @@ -1009,15 +1059,6 @@ public class KeyguardHostView extends KeyguardViewBase { return widgetCount; } - - private void setAddWidgetEnabled(boolean clickable) { - View addWidget = mAppWidgetContainer.findViewById(R.id.keyguard_add_widget); - if (addWidget != null) { - View addWidgetButton = addWidget.findViewById(R.id.keyguard_add_widget_view); - addWidgetButton.setEnabled(clickable); - } - } - private void addDefaultWidgets() { LayoutInflater inflater = LayoutInflater.from(mContext); inflater.inflate(R.layout.keyguard_transport_control_view, this, true); diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java index 76ba811aa0ae..9bc0111ea985 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java @@ -129,12 +129,19 @@ public class KeyguardViewManager { @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - if (mKeyguardHost.getVisibility() == View.VISIBLE) { - // only propagate configuration messages if we're currently showing - maybeCreateKeyguardLocked(shouldEnableScreenRotation(), true, null); - } else { - if (DEBUG) Log.v(TAG, "onConfigurationChanged: view not visible"); - } + post(new Runnable() { + @Override + public void run() { + synchronized (KeyguardViewManager.this) { + if (mKeyguardHost.getVisibility() == View.VISIBLE) { + // only propagate configuration messages if we're currently showing + maybeCreateKeyguardLocked(shouldEnableScreenRotation(), true, null); + } else { + if (DEBUG) Log.v(TAG, "onConfigurationChanged: view not visible"); + } + } + } + }); } @Override @@ -236,12 +243,6 @@ public class KeyguardViewManager { } if (options != null) { - if (options.getBoolean(LockPatternUtils.KEYGUARD_SHOW_USER_SWITCHER)) { - mKeyguardView.goToUserSwitcher(); - } - if (options.getBoolean(LockPatternUtils.KEYGUARD_SHOW_SECURITY_CHALLENGE)) { - mKeyguardView.showNextSecurityScreenIfPresent(); - } int widgetToShow = options.getInt(LockPatternUtils.KEYGUARD_SHOW_APPWIDGET, AppWidgetManager.INVALID_APPWIDGET_ID); if (widgetToShow != AppWidgetManager.INVALID_APPWIDGET_ID) { diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java index c227619fc94b..7d757ffc37d0 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java @@ -315,10 +315,7 @@ public class KeyguardViewMediator { // We need to force a reset of the views, since lockNow (called by // ActivityManagerService) will not reconstruct the keyguard if it is already showing. synchronized (KeyguardViewMediator.this) { - Bundle options = new Bundle(); - options.putBoolean(LockPatternUtils.KEYGUARD_SHOW_USER_SWITCHER, true); - options.putBoolean(LockPatternUtils.KEYGUARD_SHOW_SECURITY_CHALLENGE, true); - resetStateLocked(options); + resetStateLocked(null); adjustStatusBarLocked(); // Disable face unlock when the user switches. KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(false); diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetCarousel.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetCarousel.java index debf765cf1b2..257fd2748b14 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetCarousel.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetCarousel.java @@ -16,7 +16,6 @@ package com.android.internal.policy.impl.keyguard; import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; @@ -27,10 +26,10 @@ import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; -import java.util.ArrayList; - import com.android.internal.R; +import java.util.ArrayList; + public class KeyguardWidgetCarousel extends KeyguardWidgetPager { private float mAdjacentPagesAngle; @@ -56,17 +55,30 @@ public class KeyguardWidgetCarousel extends KeyguardWidgetPager { return MAX_SCROLL_PROGRESS; } - public float getAlphaForPage(int screenCenter, int index) { + public float getAlphaForPage(int screenCenter, int index, boolean showSidePages) { View child = getChildAt(index); if (child == null) return 0f; + boolean inVisibleRange = index >= getNextPage() - 1 && index <= getNextPage() + 1; float scrollProgress = getScrollProgress(screenCenter, child, index); - if (!isOverScrollChild(index, scrollProgress)) { + + if (isOverScrollChild(index, scrollProgress)) { + return 1.0f; + } else if ((showSidePages && inVisibleRange) || index == getNextPage()) { scrollProgress = getBoundedScrollProgress(screenCenter, child, index); float alpha = 1.0f - 1.0f * Math.abs(scrollProgress / MAX_SCROLL_PROGRESS); return alpha; } else { - return 1.0f; + return 0f; + } + } + + public float getOutlineAlphaForPage(int screenCenter, int index, boolean showSidePages) { + boolean inVisibleRange = index >= getNextPage() - 1 && index <= getNextPage() + 1; + if (inVisibleRange) { + return super.getOutlineAlphaForPage(screenCenter, index, showSidePages); + } else { + return 0f; } } @@ -75,24 +87,32 @@ public class KeyguardWidgetCarousel extends KeyguardWidgetPager { mChildrenOutlineFadeAnimation.cancel(); mChildrenOutlineFadeAnimation = null; } + boolean showSidePages = mShowingInitialHints || isPageMoving(); if (!isReordering(false)) { for (int i = 0; i < getChildCount(); i++) { KeyguardWidgetFrame child = getWidgetPageAt(i); if (child != null) { - child.setBackgroundAlpha(getOutlineAlphaForPage(screenCenter, i)); - child.setContentAlpha(getAlphaForPage(screenCenter, i)); + float outlineAlpha = getOutlineAlphaForPage(screenCenter, i, showSidePages); + float contentAlpha = getAlphaForPage(screenCenter, i,showSidePages); + child.setBackgroundAlpha(outlineAlpha); + child.setContentAlpha(contentAlpha); } } } } public void showInitialPageHints() { + mShowingInitialHints = true; int count = getChildCount(); for (int i = 0; i < count; i++) { + boolean inVisibleRange = i >= getNextPage() - 1 && i <= getNextPage() + 1; KeyguardWidgetFrame child = getWidgetPageAt(i); - if (i >= mCurrentPage - 1 && i <= mCurrentPage + 1) { - child.fadeFrame(this, true, KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER, - CHILDREN_OUTLINE_FADE_IN_DURATION); + if (inVisibleRange) { + child.setBackgroundAlpha(KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER); + child.setContentAlpha(1f); + } else { + child.setBackgroundAlpha(0f); + child.setContentAlpha(0f); } } } @@ -220,8 +240,8 @@ public class KeyguardWidgetCarousel extends KeyguardWidgetPager { for (int i = 0; i < count; i++) { KeyguardWidgetFrame child = getWidgetPageAt(i); - float finalAlpha = getAlphaForPage(mScreenCenter, i); - float finalOutlineAlpha = getOutlineAlphaForPage(mScreenCenter, i); + float finalAlpha = getAlphaForPage(mScreenCenter, i, true); + float finalOutlineAlpha = getOutlineAlphaForPage(mScreenCenter, i, true); getTransformForPage(mScreenCenter, i, mTmpTransform); boolean inVisibleRange = (i >= mCurrentPage - 1 && i <= mCurrentPage + 1); diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java index 3c792064a392..babb9cb8de19 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java @@ -424,7 +424,9 @@ public class KeyguardWidgetFrame extends FrameLayout { mBgAlphaController = caller; } - if (mBgAlphaController != caller) return; + if (mBgAlphaController != caller && mBgAlphaController != null) { + return; + } if (mFrameFade != null) { mFrameFade.cancel(); @@ -512,6 +514,10 @@ public class KeyguardWidgetFrame extends FrameLayout { return false; } + public void onBouncerShowing(boolean showing) { + // hook for subclasses + } + public void setWorkerHandler(Handler workerHandler) { mWorkerHandler = workerHandler; } @@ -519,4 +525,5 @@ public class KeyguardWidgetFrame extends FrameLayout { public Handler getWorkerHandler() { return mWorkerHandler; } + } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java index 25e278182f44..b4fe0c7aaca4 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java @@ -70,6 +70,12 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit private Callbacks mCallbacks; private int mWidgetToResetAfterFadeOut; + protected boolean mShowingInitialHints = false; + + // A temporary handle to the Add-Widget view + private View mAddWidgetView; + private int mLastWidthMeasureSpec; + private int mLastHeightMeasureSpec; // Bouncer private int mBouncerZoomInOutDuration = 250; @@ -237,18 +243,18 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit public void userActivity(); public void onUserActivityTimeoutChanged(); public void onAddView(View v); - public void onRemoveView(View v); + public void onRemoveView(View v, boolean deletePermanently); + public void onRemoveViewAnimationCompleted(); } public void addWidget(View widget) { addWidget(widget, -1); } - - public void onRemoveView(View v) { + public void onRemoveView(View v, final boolean deletePermanently) { final int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId(); if (mCallbacks != null) { - mCallbacks.onRemoveView(v); + mCallbacks.onRemoveView(v, deletePermanently); } mBackgroundWorkerHandler.post(new Runnable() { @Override @@ -258,6 +264,13 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit }); } + @Override + public void onRemoveViewAnimationCompleted() { + if (mCallbacks != null) { + mCallbacks.onRemoveViewAnimationCompleted(); + } + } + public void onAddView(View v, final int index) { final int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId(); final int[] pagesRange = new int[mTempVisiblePagesRange.length]; @@ -458,12 +471,21 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit private void updatePageAlphaValues(int screenCenter) { } - public float getAlphaForPage(int screenCenter, int index) { - return 1f; + public float getAlphaForPage(int screenCenter, int index, boolean showSidePages) { + if (showSidePages) { + return 1f; + } else { + return index == mCurrentPage ? 1.0f : 0f; + } } - public float getOutlineAlphaForPage(int screenCenter, int index) { - return getAlphaForPage(screenCenter, index) * KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER; + public float getOutlineAlphaForPage(int screenCenter, int index, boolean showSidePages) { + if (showSidePages) { + return getAlphaForPage(screenCenter, index, showSidePages) + * KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER; + } else { + return 0f; + } } protected boolean isOverScrollChild(int index, float scrollProgress) { @@ -562,12 +584,12 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit } public void showInitialPageHints() { + mShowingInitialHints = true; int count = getChildCount(); for (int i = 0; i < count; i++) { KeyguardWidgetFrame child = getWidgetPageAt(i); if (i != mCurrentPage) { - child.fadeFrame(this, true, KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER, - CHILDREN_OUTLINE_FADE_IN_DURATION); + child.setBackgroundAlpha(KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER); child.setContentAlpha(0f); } else { child.setBackgroundAlpha(0f); @@ -576,10 +598,6 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit } } - public void showSidePageHints() { - animateOutlinesAndSidePages(true, -1); - } - @Override void setCurrentPage(int currentPage) { super.setCurrentPage(currentPage); @@ -592,12 +610,10 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit mHasMeasure = false; } - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - } - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + mLastWidthMeasureSpec = widthMeasureSpec; + mLastHeightMeasureSpec = heightMeasureSpec; + int maxChallengeTop = -1; View parent = (View) getParent(); boolean challengeShowing = false; @@ -658,7 +674,7 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit for (int i = 0; i < count; i++) { float finalContentAlpha; if (show) { - finalContentAlpha = getAlphaForPage(mScreenCenter, i); + finalContentAlpha = getAlphaForPage(mScreenCenter, i, true); } else if (!show && i == curPage) { finalContentAlpha = 1f; } else { @@ -670,7 +686,7 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit ObjectAnimator a = ObjectAnimator.ofPropertyValuesHolder(child, alpha); anims.add(a); - float finalOutlineAlpha = show ? getOutlineAlphaForPage(mScreenCenter, i) : 0f; + float finalOutlineAlpha = show ? getOutlineAlphaForPage(mScreenCenter, i, true) : 0f; child.fadeFrame(this, show, finalOutlineAlpha, duration); } @@ -696,6 +712,7 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit frame.resetSize(); } mWidgetToResetAfterFadeOut = -1; + mShowingInitialHints = false; } updateWidgetFramesImportantForAccessibility(); } @@ -775,6 +792,9 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit mZoomInOutAnim.setInterpolator(new DecelerateInterpolator(1.5f)); mZoomInOutAnim.start(); } + if (currentPage instanceof KeyguardWidgetFrame) { + ((KeyguardWidgetFrame)currentPage).onBouncerShowing(false); + } } // Zoom out after the bouncer is initiated @@ -800,6 +820,27 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit mZoomInOutAnim.setInterpolator(new DecelerateInterpolator(1.5f)); mZoomInOutAnim.start(); } + if (currentPage instanceof KeyguardWidgetFrame) { + ((KeyguardWidgetFrame)currentPage).onBouncerShowing(true); + } + } + + void setAddWidgetEnabled(boolean enabled) { + if (mAddWidgetView != null && enabled) { + addView(mAddWidgetView, 0); + // We need to force measure the PagedView so that the calls to update the scroll + // position below work + measure(mLastWidthMeasureSpec, mLastHeightMeasureSpec); + // Bump up the current page to account for the addition of the new page + setCurrentPage(mCurrentPage + 1); + mAddWidgetView = null; + } else if (mAddWidgetView == null && !enabled) { + View addWidget = findViewById(com.android.internal.R.id.keyguard_add_widget); + if (addWidget != null) { + mAddWidgetView = addWidget; + removeView(addWidget); + } + } } boolean isAddPage(int pageIndex) { diff --git a/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java b/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java index 3900ab4475d9..33c2456180c3 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java @@ -1457,7 +1457,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } removeView(mDragView); - onRemoveView(mDragView); + onRemoveView(mDragView, false); addView(mDragView, pageUnderPointIndex); onAddView(mDragView, pageUnderPointIndex); mSidePageHoverIndex = -1; @@ -1587,7 +1587,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } //public abstract void onFlingToDelete(View v); - public abstract void onRemoveView(View v); + public abstract void onRemoveView(View v, boolean deletePermanently); + public abstract void onRemoveViewAnimationCompleted(); public abstract void onAddView(View v, int index); private void resetTouchState() { @@ -2383,6 +2384,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc public void run() { mDeferringForDelete = false; onEndReordering(); + onRemoveViewAnimationCompleted(); } }; zoomIn(onCompleteRunnable); @@ -2391,7 +2393,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc slideAnimations.start(); removeView(dragView); - onRemoveView(dragView); + onRemoveView(dragView, true); } }; } diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java index 440f8e1c08cf..cbd00f3e9ac7 100644 --- a/services/java/com/android/server/AlarmManagerService.java +++ b/services/java/com/android/server/AlarmManagerService.java @@ -22,6 +22,7 @@ import android.app.AlarmManager; import android.app.IAlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -38,6 +39,7 @@ import android.os.UserHandle; import android.os.WorkSource; import android.text.TextUtils; import android.text.format.Time; +import android.util.Pair; import android.util.Slog; import android.util.TimeUtils; @@ -45,16 +47,18 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.Iterator; -import java.util.LinkedList; import java.util.Map; import java.util.TimeZone; +import com.android.internal.util.LocalLog; + class AlarmManagerService extends IAlarmManager.Stub { // The threshold for how long an alarm can be late before we print a // warning message. The time duration is in milliseconds. @@ -79,7 +83,9 @@ class AlarmManagerService extends IAlarmManager.Stub { = new Intent().addFlags(Intent.FLAG_FROM_BACKGROUND); private final Context mContext; - + + private final LocalLog mLog = new LocalLog(TAG); + private Object mLock = new Object(); private final ArrayList<Alarm> mRtcWakeupAlarms = new ArrayList<Alarm>(); @@ -91,7 +97,7 @@ class AlarmManagerService extends IAlarmManager.Stub { private int mDescriptor; private int mBroadcastRefCount = 0; private PowerManager.WakeLock mWakeLock; - private LinkedList<PendingIntent> mInFlight = new LinkedList<PendingIntent>(); + private ArrayList<InFlight> mInFlight = new ArrayList<InFlight>(); private final AlarmThread mWaitThread = new AlarmThread(); private final AlarmHandler mHandler = new AlarmHandler(); private ClockReceiver mClockReceiver; @@ -99,18 +105,59 @@ class AlarmManagerService extends IAlarmManager.Stub { private final ResultReceiver mResultReceiver = new ResultReceiver(); private final PendingIntent mTimeTickSender; private final PendingIntent mDateChangeSender; - + + private static final class InFlight extends Intent { + final PendingIntent mPendingIntent; + final Pair<String, ComponentName> mTarget; + final BroadcastStats mBroadcastStats; + final FilterStats mFilterStats; + + InFlight(AlarmManagerService service, PendingIntent pendingIntent) { + mPendingIntent = pendingIntent; + Intent intent = pendingIntent.getIntent(); + mTarget = intent != null + ? new Pair<String, ComponentName>(intent.getAction(), intent.getComponent()) + : null; + mBroadcastStats = service.getStatsLocked(pendingIntent); + FilterStats fs = mBroadcastStats.filterStats.get(mTarget); + if (fs == null) { + fs = new FilterStats(mBroadcastStats, mTarget); + mBroadcastStats.filterStats.put(mTarget, fs); + } + mFilterStats = fs; + } + } + private static final class FilterStats { + final BroadcastStats mBroadcastStats; + final Pair<String, ComponentName> mTarget; + + long aggregateTime; int count; + int numWakeup; + long startTime; + int nesting; + + FilterStats(BroadcastStats broadcastStats, Pair<String, ComponentName> target) { + mBroadcastStats = broadcastStats; + mTarget = target; + } } private static final class BroadcastStats { + final String mPackageName; + long aggregateTime; + int count; int numWakeup; long startTime; int nesting; - HashMap<Intent.FilterComparison, FilterStats> filterStats - = new HashMap<Intent.FilterComparison, FilterStats>(); + final HashMap<Pair<String, ComponentName>, FilterStats> filterStats + = new HashMap<Pair<String, ComponentName>, FilterStats>(); + + BroadcastStats(String packageName) { + mPackageName = packageName; + } } private final HashMap<String, BroadcastStats> mBroadcastStats @@ -496,24 +543,104 @@ class AlarmManagerService extends IAlarmManager.Stub { dumpAlarmList(pw, mElapsedRealtimeAlarms, " ", "ELAPSED", now); } } - - pw.println(" "); + + pw.println(); pw.print(" Broadcast ref count: "); pw.println(mBroadcastRefCount); - + pw.println(); + + if (mLog.dump(pw, " Recent problems", " ")) { + pw.println(); + } + + final FilterStats[] topFilters = new FilterStats[10]; + final Comparator<FilterStats> comparator = new Comparator<FilterStats>() { + @Override + public int compare(FilterStats lhs, FilterStats rhs) { + if (lhs.aggregateTime < rhs.aggregateTime) { + return 1; + } else if (lhs.aggregateTime > rhs.aggregateTime) { + return -1; + } + return 0; + } + }; + int len = 0; + for (Map.Entry<String, BroadcastStats> be : mBroadcastStats.entrySet()) { + BroadcastStats bs = be.getValue(); + for (Map.Entry<Pair<String, ComponentName>, FilterStats> fe + : bs.filterStats.entrySet()) { + FilterStats fs = fe.getValue(); + int pos = len > 0 + ? Arrays.binarySearch(topFilters, 0, len, fs, comparator) : 0; + if (pos < 0) { + pos = -pos - 1; + } + if (pos < topFilters.length) { + int copylen = topFilters.length - pos - 1; + if (copylen > 0) { + System.arraycopy(topFilters, pos, topFilters, pos+1, copylen); + } + topFilters[pos] = fs; + if (len < topFilters.length) { + len++; + } + } + } + } + if (len > 0) { + pw.println(" Top Alarms:"); + for (int i=0; i<len; i++) { + FilterStats fs = topFilters[i]; + pw.print(" "); + if (fs.nesting > 0) pw.print("*ACTIVE* "); + TimeUtils.formatDuration(fs.aggregateTime, pw); + pw.print(" running, "); pw.print(fs.numWakeup); + pw.print(" wakeups, "); pw.print(fs.count); + pw.print(" alarms: "); pw.print(fs.mBroadcastStats.mPackageName); + pw.println(); + pw.print(" "); + if (fs.mTarget.first != null) { + pw.print(" act="); pw.print(fs.mTarget.first); + } + if (fs.mTarget.second != null) { + pw.print(" cmp="); pw.print(fs.mTarget.second.toShortString()); + } + pw.println(); + } + } + pw.println(" "); pw.println(" Alarm Stats:"); + final ArrayList<FilterStats> tmpFilters = new ArrayList<FilterStats>(); for (Map.Entry<String, BroadcastStats> be : mBroadcastStats.entrySet()) { BroadcastStats bs = be.getValue(); - pw.print(" "); pw.println(be.getKey()); - pw.print(" "); pw.print(bs.aggregateTime); - pw.print("ms running, "); pw.print(bs.numWakeup); - pw.println(" wakeups"); - for (Map.Entry<Intent.FilterComparison, FilterStats> fe + pw.print(" "); + if (bs.nesting > 0) pw.print("*ACTIVE* "); + pw.print(be.getKey()); + pw.print(" "); TimeUtils.formatDuration(bs.aggregateTime, pw); + pw.print(" running, "); pw.print(bs.numWakeup); + pw.println(" wakeups:"); + tmpFilters.clear(); + for (Map.Entry<Pair<String, ComponentName>, FilterStats> fe : bs.filterStats.entrySet()) { - pw.print(" "); pw.print(fe.getValue().count); - pw.print(" alarms: "); - pw.println(fe.getKey().getIntent().toShortString( - false, true, false, true)); + tmpFilters.add(fe.getValue()); + } + Collections.sort(tmpFilters, comparator); + for (int i=0; i<tmpFilters.size(); i++) { + FilterStats fs = tmpFilters.get(i); + pw.print(" "); + if (fs.nesting > 0) pw.print("*ACTIVE* "); + TimeUtils.formatDuration(fs.aggregateTime, pw); + pw.print(" "); pw.print(fs.numWakeup); + pw.print(" wakes " ); pw.print(fs.count); + pw.print(" alarms:"); + if (fs.mTarget.first != null) { + pw.print(" act="); pw.print(fs.mTarget.first); + } + if (fs.mTarget.second != null) { + pw.print(" cmp="); pw.print(fs.mTarget.second.toShortString()); + } + pw.println(); } } } @@ -708,18 +835,31 @@ class AlarmManagerService extends IAlarmManager.Stub { setWakelockWorkSource(alarm.operation); mWakeLock.acquire(); } - mInFlight.add(alarm.operation); + final InFlight inflight = new InFlight(AlarmManagerService.this, + alarm.operation); + mInFlight.add(inflight); mBroadcastRefCount++; - - BroadcastStats bs = getStatsLocked(alarm.operation); + + final BroadcastStats bs = inflight.mBroadcastStats; + bs.count++; if (bs.nesting == 0) { + bs.nesting = 1; bs.startTime = nowELAPSED; } else { bs.nesting++; } + final FilterStats fs = inflight.mFilterStats; + fs.count++; + if (fs.nesting == 0) { + fs.nesting = 1; + fs.startTime = nowELAPSED; + } else { + fs.nesting++; + } if (alarm.type == AlarmManager.ELAPSED_REALTIME_WAKEUP || alarm.type == AlarmManager.RTC_WAKEUP) { bs.numWakeup++; + fs.numWakeup++; ActivityManagerNative.noteWakeupAlarm( alarm.operation); } @@ -908,44 +1048,58 @@ class AlarmManagerService extends IAlarmManager.Stub { String pkg = pi.getTargetPackage(); BroadcastStats bs = mBroadcastStats.get(pkg); if (bs == null) { - bs = new BroadcastStats(); + bs = new BroadcastStats(pkg); mBroadcastStats.put(pkg, bs); } return bs; } - + class ResultReceiver implements PendingIntent.OnFinished { public void onSendFinished(PendingIntent pi, Intent intent, int resultCode, String resultData, Bundle resultExtras) { synchronized (mLock) { - BroadcastStats bs = getStatsLocked(pi); - if (bs != null) { + InFlight inflight = null; + for (int i=0; i<mInFlight.size(); i++) { + if (mInFlight.get(i).mPendingIntent == pi) { + inflight = mInFlight.remove(i); + break; + } + } + if (inflight != null) { + final long nowELAPSED = SystemClock.elapsedRealtime(); + BroadcastStats bs = inflight.mBroadcastStats; bs.nesting--; if (bs.nesting <= 0) { bs.nesting = 0; - bs.aggregateTime += SystemClock.elapsedRealtime() - - bs.startTime; - Intent.FilterComparison fc = new Intent.FilterComparison(intent); - FilterStats fs = bs.filterStats.get(fc); - if (fs == null) { - fs = new FilterStats(); - bs.filterStats.put(fc, fs); - } - fs.count++; + bs.aggregateTime += nowELAPSED - bs.startTime; + } + FilterStats fs = inflight.mFilterStats; + fs.nesting--; + if (fs.nesting <= 0) { + fs.nesting = 0; + fs.aggregateTime += nowELAPSED - fs.startTime; } + } else { + mLog.w("No in-flight alarm for " + pi + " " + intent); } - mInFlight.removeFirst(); mBroadcastRefCount--; if (mBroadcastRefCount == 0) { mWakeLock.release(); + if (mInFlight.size() > 0) { + mLog.w("Finished all broadcasts with " + mInFlight.size() + + " remaining inflights"); + for (int i=0; i<mInFlight.size(); i++) { + mLog.w(" Remaining #" + i + ": " + mInFlight.get(i)); + } + mInFlight.clear(); + } } else { // the next of our alarms is now in flight. reattribute the wakelock. - final PendingIntent nowInFlight = mInFlight.peekFirst(); - if (nowInFlight != null) { - setWakelockWorkSource(nowInFlight); + if (mInFlight.size() > 0) { + setWakelockWorkSource(mInFlight.get(0).mPendingIntent); } else { // should never happen - Slog.e(TAG, "Alarm wakelock still held but sent queue empty"); + mLog.w("Alarm wakelock still held but sent queue empty"); mWakeLock.setWorkSource(null); } } diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java index 06d37dc32a39..9590712a7d38 100644 --- a/services/java/com/android/server/AppWidgetService.java +++ b/services/java/com/android/server/AppWidgetService.java @@ -26,6 +26,8 @@ import android.content.IntentFilter; import android.content.pm.PackageManager; import android.os.Binder; import android.os.Bundle; +import android.os.Handler; +import android.os.HandlerThread; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; @@ -54,13 +56,19 @@ class AppWidgetService extends IAppWidgetService.Stub Locale mLocale; PackageManager mPackageManager; boolean mSafeMode; + private final Handler mSaveStateHandler; private final SparseArray<AppWidgetServiceImpl> mAppWidgetServices; AppWidgetService(Context context) { mContext = context; + + HandlerThread handlerThread = new HandlerThread("AppWidgetService -- Save state"); + handlerThread.start(); + mSaveStateHandler = new Handler(handlerThread.getLooper()); + mAppWidgetServices = new SparseArray<AppWidgetServiceImpl>(5); - AppWidgetServiceImpl primary = new AppWidgetServiceImpl(context, 0); + AppWidgetServiceImpl primary = new AppWidgetServiceImpl(context, 0, mSaveStateHandler); mAppWidgetServices.append(0, primary); } @@ -138,6 +146,11 @@ class AppWidgetService extends IAppWidgetService.Stub return getImplForUser(getCallingOrCurrentUserId()).allocateAppWidgetId( packageName, hostId); } + + @Override + public int[] getAppWidgetIdsForHost(int hostId) throws RemoteException { + return getImplForUser(getCallingOrCurrentUserId()).getAppWidgetIdsForHost(hostId); + } @Override public void deleteAppWidgetId(int appWidgetId) throws RemoteException { @@ -229,7 +242,7 @@ class AppWidgetService extends IAppWidgetService.Stub if (service == null) { Slog.i(TAG, "Unable to find AppWidgetServiceImpl for user " + userId + ", adding"); // TODO: Verify that it's a valid user - service = new AppWidgetServiceImpl(mContext, userId); + service = new AppWidgetServiceImpl(mContext, userId, mSaveStateHandler); service.systemReady(mSafeMode); // Assume that BOOT_COMPLETED was received, as this is a non-primary user. mAppWidgetServices.append(userId, service); diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java index daa82f2928d4..bba5192d9440 100644 --- a/services/java/com/android/server/AppWidgetServiceImpl.java +++ b/services/java/com/android/server/AppWidgetServiceImpl.java @@ -41,7 +41,10 @@ import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.Environment; +import android.os.Handler; +import android.os.HandlerThread; import android.os.IBinder; +import android.os.Looper; import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; @@ -180,15 +183,18 @@ class AppWidgetServiceImpl { boolean mStateLoaded; int mMaxWidgetBitmapMemory; + private final Handler mSaveStateHandler; + // These are for debugging only -- widgets are going missing in some rare instances ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>(); ArrayList<Host> mDeletedHosts = new ArrayList<Host>(); - AppWidgetServiceImpl(Context context, int userId) { + AppWidgetServiceImpl(Context context, int userId, Handler saveStateHandler) { mContext = context; mPm = AppGlobals.getPackageManager(); mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); mUserId = userId; + mSaveStateHandler = saveStateHandler; computeMaximumWidgetBitmapMemory(); } @@ -236,7 +242,7 @@ class AppWidgetServiceImpl { updateProvidersForPackageLocked(cn.getPackageName(), removedProviders); } } - saveStateLocked(); + saveStateAsync(); } } } @@ -286,7 +292,7 @@ class AppWidgetServiceImpl { providersModified |= addProvidersForPackageLocked(pkgName); } } - saveStateLocked(); + saveStateAsync(); } } else { Bundle extras = intent.getExtras(); @@ -297,7 +303,7 @@ class AppWidgetServiceImpl { ensureStateLoadedLocked(); for (String pkgName : pkgList) { providersModified |= removeProvidersForPackageLocked(pkgName); - saveStateLocked(); + saveStateAsync(); } } } @@ -330,6 +336,7 @@ class AppWidgetServiceImpl { pw.print(info.autoAdvanceViewId); pw.print(" initialLayout=#"); pw.print(Integer.toHexString(info.initialLayout)); + pw.print(" uid="); pw.print(p.uid); pw.print(" zombie="); pw.println(p.zombie); } @@ -410,7 +417,7 @@ class AppWidgetServiceImpl { private void ensureStateLoadedLocked() { if (!mStateLoaded) { - loadAppWidgetList(); + loadAppWidgetListLocked(); loadStateLocked(); mStateLoaded = true; } @@ -431,7 +438,7 @@ class AppWidgetServiceImpl { host.instances.add(id); mAppWidgetIds.add(id); - saveStateLocked(); + saveStateAsync(); if (DBG) log("Allocating AppWidgetId for " + packageName + " host=" + hostId + " id=" + appWidgetId); return appWidgetId; @@ -444,7 +451,7 @@ class AppWidgetServiceImpl { AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId); if (id != null) { deleteAppWidgetLocked(id); - saveStateLocked(); + saveStateAsync(); } } } @@ -456,7 +463,7 @@ class AppWidgetServiceImpl { Host host = lookupHostLocked(callingUid, hostId); if (host != null) { deleteHostLocked(host); - saveStateLocked(); + saveStateAsync(); } } } @@ -475,7 +482,7 @@ class AppWidgetServiceImpl { } } if (changed) { - saveStateLocked(); + saveStateAsync(); } } } @@ -591,7 +598,7 @@ class AppWidgetServiceImpl { // schedule the future updates registerForBroadcastsLocked(p, getAppWidgetIds(p)); - saveStateLocked(); + saveStateAsync(); } } finally { Binder.restoreCallingIdentity(ident); @@ -655,8 +662,8 @@ class AppWidgetServiceImpl { } else { mPackagesWithBindWidgetPermission.remove(packageName); } + saveStateAsync(); } - saveStateLocked(); } // Binds to a specific RemoteViewsService @@ -693,6 +700,10 @@ class AppWidgetServiceImpl { } int userId = UserHandle.getUserId(id.provider.uid); + if (userId != mUserId) { + Slog.w(TAG, "AppWidgetServiceImpl of user " + mUserId + + " binding to provider on user " + userId); + } // Bind to the RemoteViewsService (which will trigger a callback to the // RemoteViewsAdapter.onServiceConnected()) final long token = Binder.clearCallingIdentity(); @@ -849,13 +860,17 @@ class AppWidgetServiceImpl { } public List<AppWidgetProviderInfo> getInstalledProviders() { + return getInstalledProviders(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN); + } + + private List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter) { synchronized (mAppWidgetIds) { ensureStateLoadedLocked(); final int N = mInstalledProviders.size(); ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N); for (int i = 0; i < N; i++) { Provider p = mInstalledProviders.get(i); - if (!p.zombie) { + if (!p.zombie && (p.info.widgetCategory & categoryFilter) != 0) { result.add(cloneIfLocalBinder(p.info)); } } @@ -893,6 +908,20 @@ class AppWidgetServiceImpl { } } + private void saveStateAsync() { + mSaveStateHandler.post(mSaveStateRunnable); + } + + private final Runnable mSaveStateRunnable = new Runnable() { + @Override + public void run() { + synchronized (mAppWidgetIds) { + ensureStateLoadedLocked(); + saveStateLocked(); + } + } + }; + public void updateAppWidgetOptions(int appWidgetId, Bundle options) { synchronized (mAppWidgetIds) { options = cloneIfLocalBinder(options); @@ -913,7 +942,7 @@ class AppWidgetServiceImpl { intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, id.options); mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId)); - saveStateLocked(); + saveStateAsync(); } } @@ -942,6 +971,13 @@ class AppWidgetServiceImpl { ensureStateLoadedLocked(); for (int i = 0; i < N; i++) { AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]); + if (id == null) { + String message = "AppWidgetId NPE: mUserId=" + mUserId + + ", callingUid=" + Binder.getCallingUid() + + ", appWidgetIds[i]=" + appWidgetIds[i] + + "\n mAppWidgets:\n" + getUserWidgets(); + throw new NullPointerException(message); + } if (id.views != null) { // Only trigger a partial update for a widget if it has received a full update updateAppWidgetInstanceLocked(id, views, true); @@ -950,6 +986,18 @@ class AppWidgetServiceImpl { } } + private String getUserWidgets() { + StringBuffer sb = new StringBuffer(); + for (AppWidgetId widget: mAppWidgetIds) { + sb.append(" id="); sb.append(widget.appWidgetId); + sb.append(", hostUid="); sb.append(widget.host.uid); + sb.append(", provider="); sb.append(widget.provider.info.provider.toString()); + sb.append("\n"); + } + sb.append("\n"); + return sb.toString(); + } + public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) { if (appWidgetIds == null) { return; @@ -1214,7 +1262,7 @@ class AppWidgetServiceImpl { } } - void loadAppWidgetList() { + void loadAppWidgetListLocked() { Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); try { List<ResolveInfo> broadcastReceivers = mPm.queryIntentReceivers(intent, @@ -1334,6 +1382,28 @@ class AppWidgetServiceImpl { } } + static int[] getAppWidgetIds(Host h) { + int instancesSize = h.instances.size(); + int appWidgetIds[] = new int[instancesSize]; + for (int i = 0; i < instancesSize; i++) { + appWidgetIds[i] = h.instances.get(i).appWidgetId; + } + return appWidgetIds; + } + + public int[] getAppWidgetIdsForHost(int hostId) { + synchronized (mAppWidgetIds) { + ensureStateLoadedLocked(); + int callingUid = Binder.getCallingUid(); + Host host = lookupHostLocked(callingUid, hostId); + if (host != null) { + return getAppWidgetIds(host); + } else { + return new int[0]; + } + } + } + private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) { Provider p = null; diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index ad1dfb279961..a7c4d7301390 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -2686,18 +2686,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { state + "/" + info.getDetailedState()); } - // Connectivity state changed: - // [31-14] Reserved for future use - // [13-10] Network subtype (for mobile network, as defined - // by TelephonyManager) - // [9-4] Detailed state ordinal (as defined by - // NetworkInfo.DetailedState) - // [3-0] Network type (as defined by ConnectivityManager) - int eventLogParam = (info.getType() & 0xf) | - ((info.getDetailedState().ordinal() & 0x3f) << 4) | - (info.getSubtype() << 10); - EventLog.writeEvent(EventLogTags.CONNECTIVITY_STATE_CHANGED, - eventLogParam); + EventLogTags.writeConnectivityStateChanged( + info.getType(), info.getSubtype(), info.getDetailedState().ordinal()); if (info.getDetailedState() == NetworkInfo.DetailedState.FAILED) { diff --git a/services/java/com/android/server/EventLogTags.logtags b/services/java/com/android/server/EventLogTags.logtags index 0fe66fccc921..8bc2da26ae92 100644 --- a/services/java/com/android/server/EventLogTags.logtags +++ b/services/java/com/android/server/EventLogTags.logtags @@ -135,12 +135,8 @@ option java_package com.android.server # --------------------------- # ConnectivityService.java # --------------------------- -# Connectivity state changed: -# [31-14] Reserved for future use -# [13-10] Network subtype (for mobile network, as defined by TelephonyManager) -# [ 9- 4] Detailed state ordinal (as defined by NetworkInfo.DetailedState) -# [ 3- 0] Network type (as defined by ConnectivityManager) -50020 connectivity_state_changed (custom|1|5) +# Connectivity state changed +50020 connectivity_state_changed (type|1),(subtype|1),(state|1) # --------------------------- diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index c9ff59551d71..8eb532d2de1f 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -386,6 +386,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private Locale mLastSystemLocale; private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor(); private final IPackageManager mIPackageManager; + private boolean mInputBoundToKeyguard; class SettingsObserver extends ContentObserver { SettingsObserver(Handler handler) { @@ -877,10 +878,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final boolean hardKeyShown = haveHardKeyboard && conf.hardKeyboardHidden != Configuration.HARDKEYBOARDHIDDEN_YES; - final boolean isScreenLocked = mKeyguardManager != null - && mKeyguardManager.isKeyguardLocked() - && mKeyguardManager.isKeyguardSecure(); - mImeWindowVis = (!isScreenLocked && (mInputShown || hardKeyShown)) ? + final boolean isScreenLocked = + mKeyguardManager != null && mKeyguardManager.isKeyguardLocked(); + final boolean isScreenSecurelyLocked = + isScreenLocked && mKeyguardManager.isKeyguardSecure(); + final boolean inputShown = mInputShown && (!isScreenLocked || mInputBoundToKeyguard); + mImeWindowVis = (!isScreenSecurelyLocked && (inputShown || hardKeyShown)) ? (InputMethodService.IME_ACTIVE | InputMethodService.IME_VISIBLE) : 0; updateImeWindowStatusLocked(); } @@ -1124,6 +1127,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return mNoBinding; } + if (mCurClient == null) { + mInputBoundToKeyguard = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked(); + if (DEBUG) { + Slog.v(TAG, "New bind. keyguard = " + mInputBoundToKeyguard); + } + } + if (mCurClient != cs) { // If the client is changing, we need to switch over to the new // one. @@ -1814,9 +1824,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub public InputBindResult windowGainedFocus(IInputMethodClient client, IBinder windowToken, int controlFlags, int softInputMode, int windowFlags, EditorInfo attribute, IInputContext inputContext) { - if (!calledFromValidUser()) { - return null; - } + // Needs to check the validity before clearing calling identity + final boolean calledFromValidUser = calledFromValidUser(); + InputBindResult res = null; long ident = Binder.clearCallingIdentity(); try { @@ -1846,6 +1856,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } catch (RemoteException e) { } + if (!calledFromValidUser) { + Slog.w(TAG, "A background user is requesting window. Hiding IME."); + Slog.w(TAG, "If you want to interect with IME, you need " + + "android.permission.INTERACT_ACROSS_USERS_FULL"); + hideCurrentInputLocked(0, null); + return null; + } + if (mCurFocusedWindow == windowToken) { Slog.w(TAG, "Window already focused, ignoring focus gain of: " + client + " attribute=" + attribute + ", token = " + windowToken); @@ -2486,10 +2504,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub map.put(id, p); // Valid system default IMEs and IMEs that have English subtypes are enabled - // by default, unless there's a hard keyboard and the system IME was explicitly - // disabled - if ((isValidSystemDefaultIme(p, mContext) || isSystemImeThatHasEnglishSubtype(p)) - && (!haveHardKeyboard || disabledSysImes.indexOf(id) < 0)) { + // by default + if ((isValidSystemDefaultIme(p, mContext) || isSystemImeThatHasEnglishSubtype(p))) { setInputMethodEnabledLocked(id, true); } diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index 89fa6d0580fb..7a55497c7f24 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -506,7 +506,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } else { Intent statusChanged = new Intent(); - statusChanged.putExtras(extras); + statusChanged.putExtras(new Bundle(extras)); statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status); try { synchronized (this) { @@ -541,7 +541,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } else { Intent locationChanged = new Intent(); - locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, location); + locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, new Location(location)); try { synchronized (this) { // synchronize to ensure incrementPendingBroadcastsLocked() diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index c512bc184503..ad28a3668213 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -57,6 +57,8 @@ import android.util.AttributeSet; import android.util.Slog; import android.util.Xml; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IMediaContainerService; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; @@ -181,13 +183,13 @@ class MountService extends IMountService.Stub /** When defined, base template for user-specific {@link StorageVolume}. */ private StorageVolume mEmulatedTemplate; - // @GuardedBy("mVolumesLock") + @GuardedBy("mVolumesLock") private final ArrayList<StorageVolume> mVolumes = Lists.newArrayList(); /** Map from path to {@link StorageVolume} */ - // @GuardedBy("mVolumesLock") + @GuardedBy("mVolumesLock") private final HashMap<String, StorageVolume> mVolumesByPath = Maps.newHashMap(); /** Map from path to state */ - // @GuardedBy("mVolumesLock") + @GuardedBy("mVolumesLock") private final HashMap<String, String> mVolumeStates = Maps.newHashMap(); private volatile boolean mSystemReady = false; @@ -198,8 +200,8 @@ class MountService extends IMountService.Stub // Used as a lock for methods that register/unregister listeners. final private ArrayList<MountServiceBinderListener> mListeners = new ArrayList<MountServiceBinderListener>(); - private CountDownLatch mConnectedSignal = new CountDownLatch(1); - private CountDownLatch mAsecsScanned = new CountDownLatch(1); + private final CountDownLatch mConnectedSignal = new CountDownLatch(1); + private final CountDownLatch mAsecsScanned = new CountDownLatch(1); private boolean mSendUmsConnectedOnBoot = false; /** @@ -495,10 +497,6 @@ class MountService extends IMountService.Stub } private void waitForLatch(CountDownLatch latch) { - if (latch == null) { - return; - } - for (;;) { try { if (latch.await(5000, TimeUnit.MILLISECONDS)) { @@ -738,14 +736,12 @@ class MountService extends IMountService.Stub * the hounds! */ mConnectedSignal.countDown(); - mConnectedSignal = null; // Let package manager load internal ASECs. mPms.scanAvailableAsecs(); // Notify people waiting for ASECs to be scanned that it's done. mAsecsScanned.countDown(); - mAsecsScanned = null; } }.start(); } @@ -2571,7 +2567,7 @@ class MountService extends IMountService.Stub } } - // @VisibleForTesting + @VisibleForTesting public static String buildObbPath(final String canonicalPath, int userId, boolean forVold) { // TODO: allow caller to provide Environment for full testing diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java index 92af9a9e6af6..5e94a9fc7679 100644 --- a/services/java/com/android/server/NativeDaemonConnector.java +++ b/services/java/com/android/server/NativeDaemonConnector.java @@ -25,6 +25,7 @@ import android.os.SystemClock; import android.util.LocalLog; import android.util.Slog; +import com.android.internal.annotations.VisibleForTesting; import com.google.android.collect.Lists; import java.nio.charset.Charsets; @@ -400,7 +401,7 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo * Append the given argument to {@link StringBuilder}, escaping as needed, * and surrounding with quotes when it contains spaces. */ - // @VisibleForTesting + @VisibleForTesting static void appendEscaped(StringBuilder builder, String arg) { final boolean hasSpaces = arg.indexOf(' ') >= 0; if (hasSpaces) { diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index 70d37bfc8285..e2be577b489f 100644 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -1077,16 +1077,27 @@ public class NotificationManagerService extends INotificationManager.Stub final AudioManager audioManager = (AudioManager) mContext .getSystemService(Context.AUDIO_SERVICE); + // sound final boolean useDefaultSound = (notification.defaults & Notification.DEFAULT_SOUND) != 0; - if (useDefaultSound || notification.sound != null) { - Uri uri; - if (useDefaultSound) { - uri = Settings.System.DEFAULT_NOTIFICATION_URI; - } else { - uri = notification.sound; - } + + Uri soundUri = null; + boolean hasValidSound = false; + + if (useDefaultSound) { + soundUri = Settings.System.DEFAULT_NOTIFICATION_URI; + + // check to see if the default notification sound is silent + ContentResolver resolver = mContext.getContentResolver(); + hasValidSound = Settings.System.getString(resolver, + Settings.System.NOTIFICATION_SOUND) != null; + } else if (notification.sound != null) { + soundUri = notification.sound; + hasValidSound = (soundUri != null); + } + + if (hasValidSound) { boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0; int audioStreamType; if (notification.audioStreamType >= 0) { @@ -1103,7 +1114,7 @@ public class NotificationManagerService extends INotificationManager.Stub try { final IRingtonePlayer player = mAudioService.getRingtonePlayer(); if (player != null) { - player.playAsync(uri, user, looping, audioStreamType); + player.playAsync(soundUri, user, looping, audioStreamType); } } catch (RemoteException e) { } finally { @@ -1117,13 +1128,13 @@ public class NotificationManagerService extends INotificationManager.Stub final boolean hasCustomVibrate = notification.vibrate != null; // new in 4.2: if there was supposed to be a sound and we're in vibrate mode, - // and no other vibration is specified, we apply the default vibration anyway + // and no other vibration is specified, we fall back to vibration final boolean convertSoundToVibration = !hasCustomVibrate - && (useDefaultSound || notification.sound != null) + && hasValidSound && (audioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE); - // The DEFAULT_VIBRATE flag trumps any custom vibration. + // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback. final boolean useDefaultVibrate = (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; @@ -1136,8 +1147,8 @@ public class NotificationManagerService extends INotificationManager.Stub // does not have the VIBRATE permission. long identity = Binder.clearCallingIdentity(); try { - mVibrator.vibrate(convertSoundToVibration ? mFallbackVibrationPattern - : mDefaultVibrationPattern, + mVibrator.vibrate(useDefaultVibrate ? mDefaultVibrationPattern + : mFallbackVibrationPattern, ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1); } finally { Binder.restoreCallingIdentity(identity); diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java index 35999eafaa7b..5c24e6781402 100644 --- a/services/java/com/android/server/am/ActiveServices.java +++ b/services/java/com/android/server/am/ActiveServices.java @@ -1090,11 +1090,8 @@ public class ActiveServices { boolean created = false; try { - mAm.mStringBuilder.setLength(0); - r.intent.getIntent().toShortString(mAm.mStringBuilder, true, false, true, false); - EventLog.writeEvent(EventLogTags.AM_CREATE_SERVICE, - r.userId, System.identityHashCode(r), r.shortName, - mAm.mStringBuilder.toString(), r.app.pid); + EventLogTags.writeAmCreateService( + r.userId, System.identityHashCode(r), r.shortName, r.app.pid); synchronized (r.stats.getBatteryStats()) { r.stats.startLaunchedLocked(); } @@ -1242,9 +1239,8 @@ public class ActiveServices { } if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down " + r + " " + r.intent); - EventLog.writeEvent(EventLogTags.AM_DESTROY_SERVICE, - r.userId, System.identityHashCode(r), r.shortName, - (r.app != null) ? r.app.pid : -1); + EventLogTags.writeAmDestroyService( + r.userId, System.identityHashCode(r), (r.app != null) ? r.app.pid : -1); mServiceMap.removeServiceByName(r.name, r.userId); mServiceMap.removeServiceByIntent(r.intent, r.userId); diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index db64a9ad34bd..82c9030a66f1 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -4764,6 +4764,18 @@ public final class ActivityManagerService extends ActivityManagerNative return false; } + public Intent getIntentForIntentSender(IIntentSender pendingResult) { + if (!(pendingResult instanceof PendingIntentRecord)) { + return null; + } + try { + PendingIntentRecord res = (PendingIntentRecord)pendingResult; + return res.key.requestIntent != null ? new Intent(res.key.requestIntent) : null; + } catch (ClassCastException e) { + } + return null; + } + public void setProcessLimit(int max) { enforceCallingPermission(android.Manifest.permission.SET_PROCESS_LIMIT, "setProcessLimit()"); @@ -14168,7 +14180,7 @@ public final class ActivityManagerService extends ActivityManagerNative // Once the internal notion of the active user has switched, we lock the device // with the option to show the user switcher on the keyguard. - mWindowManager.lockNow(LockPatternUtils.USER_SWITCH_LOCK_OPTIONS); + mWindowManager.lockNow(null); final UserStartedState uss = mStartedUsers.get(userId); diff --git a/services/java/com/android/server/am/EventLogTags.logtags b/services/java/com/android/server/am/EventLogTags.logtags index 88c0c0330d9b..f784861a2990 100644 --- a/services/java/com/android/server/am/EventLogTags.logtags +++ b/services/java/com/android/server/am/EventLogTags.logtags @@ -63,9 +63,9 @@ option java_package com.android.server.am 30024 am_broadcast_discard_filter (User|1|5),(Broadcast|1|5),(Action|3),(Receiver Number|1|1),(BroadcastFilter|1|5) 30025 am_broadcast_discard_app (User|1|5),(Broadcast|1|5),(Action|3),(Receiver Number|1|1),(App|3) # A service is being created -30030 am_create_service (User|1|5),(Service Record|1|5),(Name|3),(Intent|3),(PID|1|5) +30030 am_create_service (User|1|5),(Service Record|1|5),(Name|3),(PID|1|5) # A service is being destroyed -30031 am_destroy_service (User|1|5),(Service Record|1|5),(Name|3),(PID|1|5) +30031 am_destroy_service (User|1|5),(Service Record|1|5),(PID|1|5) # A process has crashed too many times, it is being cleared 30032 am_process_crashed_too_much (User|1|5),(Name|3),(PID|1|5) # An unknown process is trying to attach to the activity manager diff --git a/services/java/com/android/server/display/PersistentDataStore.java b/services/java/com/android/server/display/PersistentDataStore.java index 3a6e1a6948a4..105c2534c574 100644 --- a/services/java/com/android/server/display/PersistentDataStore.java +++ b/services/java/com/android/server/display/PersistentDataStore.java @@ -81,6 +81,15 @@ final class PersistentDataStore { } } + public WifiDisplay getRememberedWifiDisplay(String deviceAddress) { + loadIfNeeded(); + int index = findRememberedWifiDisplay(deviceAddress); + if (index >= 0) { + return mRememberedWifiDisplays.get(index); + } + return null; + } + public WifiDisplay[] getRememberedWifiDisplays() { loadIfNeeded(); return mRememberedWifiDisplays.toArray(new WifiDisplay[mRememberedWifiDisplays.size()]); @@ -137,22 +146,6 @@ final class PersistentDataStore { return true; } - public boolean renameWifiDisplay(String deviceAddress, String alias) { - int index = findRememberedWifiDisplay(deviceAddress); - if (index >= 0) { - WifiDisplay display = mRememberedWifiDisplays.get(index); - if (Objects.equal(display.getDeviceAlias(), alias)) { - return false; // already has this alias - } - WifiDisplay renamedDisplay = new WifiDisplay(deviceAddress, - display.getDeviceName(), alias); - mRememberedWifiDisplays.set(index, renamedDisplay); - setDirty(); - return true; - } - return false; - } - public boolean forgetWifiDisplay(String deviceAddress) { int index = findRememberedWifiDisplay(deviceAddress); if (index >= 0) { diff --git a/services/java/com/android/server/display/WifiDisplayAdapter.java b/services/java/com/android/server/display/WifiDisplayAdapter.java index 45fff303b7c6..c8a44d20554f 100644 --- a/services/java/com/android/server/display/WifiDisplayAdapter.java +++ b/services/java/com/android/server/display/WifiDisplayAdapter.java @@ -45,6 +45,8 @@ import android.view.Surface; import java.io.PrintWriter; import java.util.Arrays; +import libcore.util.Objects; + /** * Connects to Wifi displays that implement the Miracast protocol. * <p> @@ -224,16 +226,18 @@ final class WifiDisplayAdapter extends DisplayAdapter { } } - if (mPersistentDataStore.renameWifiDisplay(address, alias)) { - mPersistentDataStore.saveIfNeeded(); - updateRememberedDisplaysLocked(); - scheduleStatusChangedBroadcastLocked(); + WifiDisplay display = mPersistentDataStore.getRememberedWifiDisplay(address); + if (display != null && !Objects.equal(display.getDeviceAlias(), alias)) { + display = new WifiDisplay(address, display.getDeviceName(), alias); + if (mPersistentDataStore.rememberWifiDisplay(display)) { + mPersistentDataStore.saveIfNeeded(); + updateRememberedDisplaysLocked(); + scheduleStatusChangedBroadcastLocked(); + } } - if (mActiveDisplay != null && mActiveDisplay.getDeviceAddress().equals(address) - && mDisplayDevice != null) { - mDisplayDevice.setNameLocked(mActiveDisplay.getFriendlyDisplayName()); - sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_CHANGED); + if (mActiveDisplay != null && mActiveDisplay.getDeviceAddress().equals(address)) { + renameDisplayDeviceLocked(mActiveDisplay.getFriendlyDisplayName()); } } @@ -272,9 +276,42 @@ final class WifiDisplayAdapter extends DisplayAdapter { mAvailableDisplays = mPersistentDataStore.applyWifiDisplayAliases(mAvailableDisplays); } - private void handleConnectLocked(WifiDisplay display, + private void fixRememberedDisplayNamesFromAvailableDisplaysLocked() { + // It may happen that a display name has changed since it was remembered. + // Consult the list of available displays and update the name if needed. + // We don't do anything special for the active display here. The display + // controller will send a separate event when it needs to be updates. + boolean changed = false; + for (int i = 0; i < mRememberedDisplays.length; i++) { + WifiDisplay rememberedDisplay = mRememberedDisplays[i]; + WifiDisplay availableDisplay = findAvailableDisplayLocked( + rememberedDisplay.getDeviceAddress()); + if (availableDisplay != null && !rememberedDisplay.equals(availableDisplay)) { + if (DEBUG) { + Slog.d(TAG, "fixRememberedDisplayNamesFromAvailableDisplaysLocked: " + + "updating remembered display to " + availableDisplay); + } + mRememberedDisplays[i] = availableDisplay; + changed |= mPersistentDataStore.rememberWifiDisplay(availableDisplay); + } + } + if (changed) { + mPersistentDataStore.saveIfNeeded(); + } + } + + private WifiDisplay findAvailableDisplayLocked(String address) { + for (WifiDisplay display : mAvailableDisplays) { + if (display.getDeviceAddress().equals(address)) { + return display; + } + } + return null; + } + + private void addDisplayDeviceLocked(WifiDisplay display, Surface surface, int width, int height, int flags) { - handleDisconnectLocked(); + removeDisplayDeviceLocked(); if (mPersistentDataStore.rememberWifiDisplay(display)) { mPersistentDataStore.saveIfNeeded(); @@ -303,7 +340,7 @@ final class WifiDisplayAdapter extends DisplayAdapter { scheduleUpdateNotificationLocked(); } - private void handleDisconnectLocked() { + private void removeDisplayDeviceLocked() { if (mDisplayDevice != null) { mDisplayDevice.clearSurfaceLocked(); sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_REMOVED); @@ -313,6 +350,13 @@ final class WifiDisplayAdapter extends DisplayAdapter { } } + private void renameDisplayDeviceLocked(String name) { + if (mDisplayDevice != null && !mDisplayDevice.getNameLocked().equals(name)) { + mDisplayDevice.setNameLocked(name); + sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_CHANGED); + } + } + private void scheduleStatusChangedBroadcastLocked() { mCurrentStatus = null; if (!mPendingStatusChangeBroadcast) { @@ -446,6 +490,7 @@ final class WifiDisplayAdapter extends DisplayAdapter { || !Arrays.equals(mAvailableDisplays, availableDisplays)) { mScanState = WifiDisplayStatus.SCAN_STATE_NOT_SCANNING; mAvailableDisplays = availableDisplays; + fixRememberedDisplayNamesFromAvailableDisplaysLocked(); scheduleStatusChangedBroadcastLocked(); } } @@ -483,7 +528,7 @@ final class WifiDisplayAdapter extends DisplayAdapter { int width, int height, int flags) { synchronized (getSyncRoot()) { display = mPersistentDataStore.applyWifiDisplayAlias(display); - handleConnectLocked(display, surface, width, height, flags); + addDisplayDeviceLocked(display, surface, width, height, flags); if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTED || mActiveDisplay == null @@ -496,10 +541,24 @@ final class WifiDisplayAdapter extends DisplayAdapter { } @Override + public void onDisplayChanged(WifiDisplay display) { + synchronized (getSyncRoot()) { + display = mPersistentDataStore.applyWifiDisplayAlias(display); + if (mActiveDisplay != null + && mActiveDisplay.hasSameAddress(display) + && !mActiveDisplay.equals(display)) { + mActiveDisplay = display; + renameDisplayDeviceLocked(display.getFriendlyDisplayName()); + scheduleStatusChangedBroadcastLocked(); + } + } + } + + @Override public void onDisplayDisconnected() { // Stop listening. synchronized (getSyncRoot()) { - handleDisconnectLocked(); + removeDisplayDeviceLocked(); if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED || mActiveDisplay != null) { diff --git a/services/java/com/android/server/display/WifiDisplayController.java b/services/java/com/android/server/display/WifiDisplayController.java index 39d042f5fc61..886e0498987e 100644 --- a/services/java/com/android/server/display/WifiDisplayController.java +++ b/services/java/com/android/server/display/WifiDisplayController.java @@ -120,6 +120,12 @@ final class WifiDisplayController implements DumpUtils.Dump { // or are not trying to connect. private WifiP2pDevice mConnectingDevice; + // The device from which we are currently disconnecting. + private WifiP2pDevice mDisconnectingDevice; + + // The device to which we were previously trying to connect and are now canceling. + private WifiP2pDevice mCancelingDevice; + // The device to which we are currently connected, which means we have an active P2P group. private WifiP2pDevice mConnectedDevice; @@ -186,6 +192,7 @@ final class WifiDisplayController implements DumpUtils.Dump { updateWfdEnableState(); } + @Override public void dump(PrintWriter pw) { pw.println("mWifiDisplayOnSetting=" + mWifiDisplayOnSetting); pw.println("mWifiP2pEnabled=" + mWifiP2pEnabled); @@ -196,6 +203,8 @@ final class WifiDisplayController implements DumpUtils.Dump { pw.println("mDiscoverPeersRetriesLeft=" + mDiscoverPeersRetriesLeft); pw.println("mDesiredDevice=" + describeWifiP2pDevice(mDesiredDevice)); pw.println("mConnectingDisplay=" + describeWifiP2pDevice(mConnectingDevice)); + pw.println("mDisconnectingDisplay=" + describeWifiP2pDevice(mDisconnectingDevice)); + pw.println("mCancelingDisplay=" + describeWifiP2pDevice(mCancelingDevice)); pw.println("mConnectedDevice=" + describeWifiP2pDevice(mConnectedDevice)); pw.println("mConnectionRetriesLeft=" + mConnectionRetriesLeft); pw.println("mRemoteDisplay=" + mRemoteDisplay); @@ -384,7 +393,9 @@ final class WifiDisplayController implements DumpUtils.Dump { final int count = mAvailableWifiDisplayPeers.size(); final WifiDisplay[] displays = WifiDisplay.CREATOR.newArray(count); for (int i = 0; i < count; i++) { - displays[i] = createWifiDisplay(mAvailableWifiDisplayPeers.get(i)); + WifiP2pDevice device = mAvailableWifiDisplayPeers.get(i); + displays[i] = createWifiDisplay(device); + updateDesiredDevice(device); } mHandler.post(new Runnable() { @@ -395,6 +406,23 @@ final class WifiDisplayController implements DumpUtils.Dump { }); } + private void updateDesiredDevice(WifiP2pDevice device) { + // Handle the case where the device to which we are connecting or connected + // may have been renamed or reported different properties in the latest scan. + final String address = device.deviceAddress; + if (mDesiredDevice != null && mDesiredDevice.deviceAddress.equals(address)) { + if (DEBUG) { + Slog.d(TAG, "updateDesiredDevice: new information " + + describeWifiP2pDevice(device)); + } + mDesiredDevice.update(device); + if (mAdvertisedDisplay != null + && mAdvertisedDisplay.getDeviceAddress().equals(address)) { + readvertiseDisplay(createWifiDisplay(mDesiredDevice)); + } + } + } + private void connect(final WifiP2pDevice device) { if (mDesiredDevice != null && !mDesiredDevice.deviceAddress.equals(device.deviceAddress)) { @@ -459,12 +487,17 @@ final class WifiDisplayController implements DumpUtils.Dump { } // Step 2. Before we try to connect to a new device, disconnect from the old one. + if (mDisconnectingDevice != null) { + return; // wait for asynchronous callback + } if (mConnectedDevice != null && mConnectedDevice != mDesiredDevice) { Slog.i(TAG, "Disconnecting from Wifi display: " + mConnectedDevice.deviceName); + mDisconnectingDevice = mConnectedDevice; + mConnectedDevice = null; unadvertiseDisplay(); - final WifiP2pDevice oldDevice = mConnectedDevice; + final WifiP2pDevice oldDevice = mDisconnectingDevice; mWifiP2pManager.removeGroup(mWifiP2pChannel, new ActionListener() { @Override public void onSuccess() { @@ -480,8 +513,8 @@ final class WifiDisplayController implements DumpUtils.Dump { } private void next() { - if (mConnectedDevice == oldDevice) { - mConnectedDevice = null; + if (mDisconnectingDevice == oldDevice) { + mDisconnectingDevice = null; updateConnection(); } } @@ -491,13 +524,18 @@ final class WifiDisplayController implements DumpUtils.Dump { // Step 3. Before we try to connect to a new device, stop trying to connect // to the old one. + if (mCancelingDevice != null) { + return; // wait for asynchronous callback + } if (mConnectingDevice != null && mConnectingDevice != mDesiredDevice) { Slog.i(TAG, "Canceling connection to Wifi display: " + mConnectingDevice.deviceName); + mCancelingDevice = mConnectingDevice; + mConnectingDevice = null; unadvertiseDisplay(); mHandler.removeCallbacks(mConnectionTimeout); - final WifiP2pDevice oldDevice = mConnectingDevice; + final WifiP2pDevice oldDevice = mCancelingDevice; mWifiP2pManager.cancelConnect(mWifiP2pChannel, new ActionListener() { @Override public void onSuccess() { @@ -513,8 +551,8 @@ final class WifiDisplayController implements DumpUtils.Dump { } private void next() { - if (mConnectingDevice == oldDevice) { - mConnectingDevice = null; + if (mCancelingDevice == oldDevice) { + mCancelingDevice = null; updateConnection(); } } @@ -763,13 +801,17 @@ final class WifiDisplayController implements DumpUtils.Dump { public void run() { if (oldSurface != null && surface != oldSurface) { mListener.onDisplayDisconnected(); - } else if (oldDisplay != null && !Objects.equal(display, oldDisplay)) { + } else if (oldDisplay != null && !oldDisplay.hasSameAddress(display)) { mListener.onDisplayConnectionFailed(); } if (display != null) { - if (!Objects.equal(display, oldDisplay)) { + if (!display.hasSameAddress(oldDisplay)) { mListener.onDisplayConnecting(display); + } else if (!display.equals(oldDisplay)) { + // The address is the same but some other property such as the + // name must have changed. + mListener.onDisplayChanged(display); } if (surface != null && surface != oldSurface) { mListener.onDisplayConnected(display, surface, width, height, flags); @@ -784,6 +826,12 @@ final class WifiDisplayController implements DumpUtils.Dump { advertiseDisplay(null, null, 0, 0, 0); } + private void readvertiseDisplay(WifiDisplay display) { + advertiseDisplay(display, mAdvertisedDisplaySurface, + mAdvertisedDisplayWidth, mAdvertisedDisplayHeight, + mAdvertisedDisplayFlags); + } + private static Inet4Address getInterfaceAddress(WifiP2pGroup info) { NetworkInterface iface; try { @@ -885,6 +933,7 @@ final class WifiDisplayController implements DumpUtils.Dump { void onDisplayConnecting(WifiDisplay display); void onDisplayConnectionFailed(); + void onDisplayChanged(WifiDisplay display); void onDisplayConnected(WifiDisplay display, Surface surface, int width, int height, int flags); void onDisplayDisconnected(); diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java index 43ddf8d88a7b..b83933152eff 100644 --- a/services/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java @@ -131,6 +131,7 @@ import android.util.TrustedTime; import android.util.Xml; import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Objects; @@ -184,9 +185,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final int VERSION_SWITCH_UID = 10; private static final int VERSION_LATEST = VERSION_SWITCH_UID; - // @VisibleForTesting + @VisibleForTesting public static final int TYPE_WARNING = 0x1; + @VisibleForTesting public static final int TYPE_LIMIT = 0x2; + @VisibleForTesting public static final int TYPE_LIMIT_SNOOZED = 0x3; private static final String TAG_POLICY_LIST = "policy-list"; @@ -214,10 +217,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final String TAG_ALLOW_BACKGROUND = TAG + ":allowBackground"; - // @VisibleForTesting - public static final String ACTION_ALLOW_BACKGROUND = + private static final String ACTION_ALLOW_BACKGROUND = "com.android.server.net.action.ALLOW_BACKGROUND"; - public static final String ACTION_SNOOZE_WARNING = + private static final String ACTION_SNOOZE_WARNING = "com.android.server.net.action.SNOOZE_WARNING"; private static final long TIME_CACHE_MAX_AGE = DAY_IN_MILLIS; @@ -2063,7 +2065,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return intent; } - // @VisibleForTesting + @VisibleForTesting public void addIdleHandler(IdleHandler handler) { mHandler.getLooper().getQueue().addIdleHandler(handler); } diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java index a3b44e26d828..67eaad380549 100644 --- a/services/java/com/android/server/net/NetworkStatsService.java +++ b/services/java/com/android/server/net/NetworkStatsService.java @@ -115,6 +115,7 @@ import android.util.Slog; import android.util.SparseIntArray; import android.util.TrustedTime; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FileRotator; import com.android.internal.util.IndentingPrintWriter; @@ -165,7 +166,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private IConnectivityManager mConnManager; - // @VisibleForTesting + @VisibleForTesting public static final String ACTION_NETWORK_STATS_POLL = "com.android.server.action.NETWORK_STATS_POLL"; public static final String ACTION_NETWORK_STATS_UPDATED = diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java index e05442b3b905..dbfe34d3e96b 100644 --- a/services/java/com/android/server/pm/UserManagerService.java +++ b/services/java/com/android/server/pm/UserManagerService.java @@ -16,8 +16,7 @@ package com.android.server.pm; -import com.android.internal.util.ArrayUtils; -import com.android.internal.util.FastXmlSerializer; +import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import android.app.Activity; import android.app.ActivityManager; @@ -26,7 +25,6 @@ import android.app.IStopUserCallback; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.graphics.Bitmap; @@ -34,6 +32,7 @@ import android.graphics.BitmapFactory; import android.os.Binder; import android.os.Environment; import android.os.FileUtils; +import android.os.Handler; import android.os.IUserManager; import android.os.Process; import android.os.RemoteException; @@ -42,9 +41,17 @@ import android.os.UserManager; import android.util.AtomicFile; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.util.TimeUtils; import android.util.Xml; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.FastXmlSerializer; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + import java.io.BufferedOutputStream; import java.io.File; import java.io.FileDescriptor; @@ -54,13 +61,8 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; - public class UserManagerService extends IUserManager.Stub { private static final String LOG_TAG = "UserManagerService"; @@ -86,7 +88,7 @@ public class UserManagerService extends IUserManager.Stub { private static final int MIN_USER_ID = 10; - private static final int USER_VERSION = 1; + private static final int USER_VERSION = 2; private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms @@ -95,19 +97,24 @@ public class UserManagerService extends IUserManager.Stub { private final Object mInstallLock; private final Object mPackagesLock; + private final Handler mHandler; + private final File mUsersDir; private final File mUserListFile; private final File mBaseUserPath; - private SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>(); - private HashSet<Integer> mRemovingUserIds = new HashSet<Integer>(); + private final SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>(); + + /** + * Set of user IDs being actively removed. Removed IDs linger in this set + * for several seconds to work around a VFS caching issue. + */ + // @GuardedBy("mPackagesLock") + private final SparseBooleanArray mRemovingUserIds = new SparseBooleanArray(); private int[] mUserIds; private boolean mGuestEnabled; private int mNextSerialNumber; - // This resets on a reboot. Otherwise it keeps incrementing so that user ids are - // not reused in quick succession - private int mNextUserId = MIN_USER_ID; private int mUserVersion = 0; private static UserManagerService sInstance; @@ -147,6 +154,7 @@ public class UserManagerService extends IUserManager.Stub { mPm = pm; mInstallLock = installLock; mPackagesLock = packagesLock; + mHandler = new Handler(); synchronized (mInstallLock) { synchronized (mPackagesLock) { mUsersDir = new File(dataDir, USER_INFO_DIR); @@ -190,7 +198,7 @@ public class UserManagerService extends IUserManager.Stub { if (ui.partial) { continue; } - if (!excludeDying || !mRemovingUserIds.contains(ui.id)) { + if (!excludeDying || !mRemovingUserIds.get(ui.id)) { users.add(ui); } } @@ -212,7 +220,7 @@ public class UserManagerService extends IUserManager.Stub { private UserInfo getUserInfoLocked(int userId) { UserInfo ui = mUsers.get(userId); // If it is partial and not in the process of being removed, return as unknown user. - if (ui != null && ui.partial && !mRemovingUserIds.contains(userId)) { + if (ui != null && ui.partial && !mRemovingUserIds.get(userId)) { Slog.w(LOG_TAG, "getUserInfo: unknown user #" + userId); return null; } @@ -476,8 +484,7 @@ public class UserManagerService extends IUserManager.Stub { } /** - * This fixes an incorrect initialization of user name for the owner. - * TODO: Remove in the next release. + * Upgrade steps between versions, either for fixing bugs or changing the data format. */ private void upgradeIfNecessary() { int userVersion = mUserVersion; @@ -491,6 +498,16 @@ public class UserManagerService extends IUserManager.Stub { userVersion = 1; } + if (userVersion < 2) { + // Owner should be marked as initialized + UserInfo user = mUsers.get(UserHandle.USER_OWNER); + if ((user.flags & UserInfo.FLAG_INITIALIZED) == 0) { + user.flags |= UserInfo.FLAG_INITIALIZED; + writeUserLocked(user); + } + userVersion = 2; + } + if (userVersion < USER_VERSION) { Slog.w(LOG_TAG, "User version " + mUserVersion + " didn't upgrade as expected to " + USER_VERSION); @@ -502,7 +519,7 @@ public class UserManagerService extends IUserManager.Stub { private void fallbackToSingleUserLocked() { // Create the primary user - UserInfo primary = new UserInfo(0, + UserInfo primary = new UserInfo(0, mContext.getResources().getString(com.android.internal.R.string.owner_name), null, UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY | UserInfo.FLAG_INITIALIZED); mUsers.put(0, primary); @@ -749,7 +766,7 @@ public class UserManagerService extends IUserManager.Stub { if (userHandle == 0 || user == null) { return false; } - mRemovingUserIds.add(userHandle); + mRemovingUserIds.put(userHandle, true); // Set this to a partially created user, so that the user will be purged // on next startup, in case the runtime stops now before stopping and // removing the user completely. @@ -813,13 +830,25 @@ public class UserManagerService extends IUserManager.Stub { } } - private void removeUserStateLocked(int userHandle) { + private void removeUserStateLocked(final int userHandle) { // Cleanup package manager settings mPm.cleanUpUserLILPw(userHandle); // Remove this user from the list mUsers.remove(userHandle); - mRemovingUserIds.remove(userHandle); + + // Have user ID linger for several seconds to let external storage VFS + // cache entries expire. This must be greater than the 'entry_valid' + // timeout used by the FUSE daemon. + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + synchronized (mPackagesLock) { + mRemovingUserIds.delete(userHandle); + } + } + }, MINUTE_IN_MILLIS); + // Remove user file AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + ".xml")); userFile.delete(); @@ -906,14 +935,13 @@ public class UserManagerService extends IUserManager.Stub { */ private int getNextAvailableIdLocked() { synchronized (mPackagesLock) { - int i = mNextUserId; + int i = MIN_USER_ID; while (i < Integer.MAX_VALUE) { - if (mUsers.indexOfKey(i) < 0 && !mRemovingUserIds.contains(i)) { + if (mUsers.indexOfKey(i) < 0 && !mRemovingUserIds.get(i)) { break; } i++; } - mNextUserId = i + 1; return i; } } @@ -938,7 +966,7 @@ public class UserManagerService extends IUserManager.Stub { UserInfo user = mUsers.valueAt(i); if (user == null) continue; pw.print(" "); pw.print(user); pw.print(" serialNo="); pw.print(user.serialNumber); - if (mRemovingUserIds.contains(mUsers.keyAt(i))) pw.print(" <removing> "); + if (mRemovingUserIds.get(mUsers.keyAt(i))) pw.print(" <removing> "); if (user.partial) pw.print(" <partial>"); pw.println(); pw.print(" Created: "); diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java index 8650192f1ffb..2690442065e5 100644 --- a/services/java/com/android/server/power/PowerManagerService.java +++ b/services/java/com/android/server/power/PowerManagerService.java @@ -618,8 +618,19 @@ public final class PowerManagerService extends IPowerManager.Stub } } + private static boolean isScreenLock(final WakeLock wakeLock) { + switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) { + case PowerManager.FULL_WAKE_LOCK: + case PowerManager.SCREEN_BRIGHT_WAKE_LOCK: + case PowerManager.SCREEN_DIM_WAKE_LOCK: + return true; + } + return false; + } + private void applyWakeLockFlagsOnAcquireLocked(WakeLock wakeLock) { - if ((wakeLock.mFlags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) { + if ((wakeLock.mFlags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0 && + isScreenLock(wakeLock)) { wakeUpNoUpdateLocked(SystemClock.uptimeMillis()); } } diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java index f34a52da7ad3..c7c2c6287095 100644 --- a/services/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/java/com/android/server/usb/UsbDeviceManager.java @@ -47,6 +47,8 @@ import android.provider.Settings; import android.util.Pair; import android.util.Slog; +import com.android.internal.annotations.GuardedBy; + import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; @@ -105,7 +107,7 @@ public class UsbDeviceManager { private final Context mContext; private final ContentResolver mContentResolver; - // @GuardedBy("mLock") + @GuardedBy("mLock") private UsbSettingsManager mCurrentSettings; private NotificationManager mNotificationManager; private final boolean mHasUsbAccessory; diff --git a/services/java/com/android/server/usb/UsbHostManager.java b/services/java/com/android/server/usb/UsbHostManager.java index 175ae6f0677a..10272f270f2a 100644 --- a/services/java/com/android/server/usb/UsbHostManager.java +++ b/services/java/com/android/server/usb/UsbHostManager.java @@ -26,6 +26,8 @@ import android.os.ParcelFileDescriptor; import android.os.Parcelable; import android.util.Slog; +import com.android.internal.annotations.GuardedBy; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.HashMap; @@ -46,7 +48,7 @@ public class UsbHostManager { private final Context mContext; private final Object mLock = new Object(); - // @GuardedBy("mLock") + @GuardedBy("mLock") private UsbSettingsManager mCurrentSettings; public UsbHostManager(Context context) { diff --git a/services/java/com/android/server/usb/UsbService.java b/services/java/com/android/server/usb/UsbService.java index 629f5fa4bda8..3918d15f650f 100644 --- a/services/java/com/android/server/usb/UsbService.java +++ b/services/java/com/android/server/usb/UsbService.java @@ -30,6 +30,7 @@ import android.os.ParcelFileDescriptor; import android.os.UserHandle; import android.util.SparseArray; +import com.android.internal.annotations.GuardedBy; import com.android.internal.util.IndentingPrintWriter; import java.io.File; @@ -52,7 +53,7 @@ public class UsbService extends IUsbManager.Stub { private final Object mLock = new Object(); /** Map from {@link UserHandle} to {@link UsbSettingsManager} */ - // @GuardedBy("mLock") + @GuardedBy("mLock") private final SparseArray<UsbSettingsManager> mSettingsByUser = new SparseArray<UsbSettingsManager>(); diff --git a/tests/AppLaunch/Android.mk b/tests/AppLaunch/Android.mk new file mode 100644 index 000000000000..c0560fd78e6c --- /dev/null +++ b/tests/AppLaunch/Android.mk @@ -0,0 +1,17 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +# Only compile source java files in this apk. +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_PACKAGE_NAME := AppLaunch + +LOCAL_CERTIFICATE := platform +LOCAL_JAVA_LIBRARIES := android.test.runner + +include $(BUILD_PACKAGE) + +# Use the following include to make our test apk. +include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file diff --git a/tests/AppLaunch/AndroidManifest.xml b/tests/AppLaunch/AndroidManifest.xml new file mode 100644 index 000000000000..ac6760bb8366 --- /dev/null +++ b/tests/AppLaunch/AndroidManifest.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.tests.applaunch" + android:sharedUserId="android.uid.system" > + <instrumentation android:label="Measure app start up time" + android:name="android.test.InstrumentationTestRunner" + android:targetPackage="com.android.tests.applaunch" /> + + <application android:label="App Launch Test"> + <uses-library android:name="android.test.runner" /> + </application> +</manifest>
\ No newline at end of file diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java new file mode 100644 index 000000000000..e9374e031ff5 --- /dev/null +++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.tests.applaunch; + +import android.app.ActivityManager; +import android.app.ActivityManager.ProcessErrorStateInfo; +import android.app.ActivityManagerNative; +import android.app.IActivityManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ResolveInfo; +import android.os.Bundle; +import android.os.RemoteException; +import android.os.UserHandle; +import android.test.InstrumentationTestCase; +import android.test.InstrumentationTestRunner; +import android.util.Log; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * This test is intended to measure the time it takes for the apps to start. + * Names of the applications are passed in command line, and the + * test starts each application, and reports the start up time in milliseconds. + * The instrumentation expects the following key to be passed on the command line: + * apps - A list of applications to start and their corresponding result keys + * in the following format: + * -e apps <app name>^<result key>|<app name>^<result key> + */ +public class AppLaunch extends InstrumentationTestCase { + + private static final int JOIN_TIMEOUT = 10000; + private static final String TAG = "AppLaunch"; + private static final String KEY_APPS = "apps"; + + private Map<String, Intent> mNameToIntent; + private Map<String, String> mNameToProcess; + private Map<String, String> mNameToResultKey; + + private IActivityManager mAm; + + public void testMeasureStartUpTime() throws RemoteException { + InstrumentationTestRunner instrumentation = + (InstrumentationTestRunner)getInstrumentation(); + Bundle args = instrumentation.getBundle(); + mAm = ActivityManagerNative.getDefault(); + + createMappings(); + parseArgs(args); + + Bundle results = new Bundle(); + for (String app : mNameToResultKey.keySet()) { + try { + startApp(app, results); + closeApp(); + } catch (NameNotFoundException e) { + Log.i(TAG, "Application " + app + " not found"); + } + + } + instrumentation.sendStatus(0, results); + } + + private void parseArgs(Bundle args) { + mNameToResultKey = new HashMap<String, String>(); + String appList = args.getString(KEY_APPS); + + if (appList == null) + return; + + String appNames[] = appList.split("\\|"); + for (String pair : appNames) { + String[] parts = pair.split("\\^"); + if (parts.length != 2) { + Log.e(TAG, "The apps key is incorectly formatted"); + fail(); + } + + mNameToResultKey.put(parts[0], parts[1]); + } + } + + private void createMappings() { + mNameToIntent = new HashMap<String, Intent>(); + mNameToProcess = new HashMap<String, String>(); + + PackageManager pm = getInstrumentation().getContext() + .getPackageManager(); + Intent intentToResolve = new Intent(Intent.ACTION_MAIN); + intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER); + List<ResolveInfo> ris = pm.queryIntentActivities(intentToResolve, 0); + if (ris == null || ris.isEmpty()) { + Log.i(TAG, "Could not find any apps"); + } else { + for (ResolveInfo ri : ris) { + Intent startIntent = new Intent(intentToResolve); + startIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + startIntent.setClassName(ri.activityInfo.packageName, + ri.activityInfo.name); + mNameToIntent.put(ri.loadLabel(pm).toString(), startIntent); + mNameToProcess.put(ri.loadLabel(pm).toString(), + ri.activityInfo.processName); + } + } + } + + private void startApp(String appName, Bundle results) + throws NameNotFoundException, RemoteException { + Log.i(TAG, "Starting " + appName); + + Intent startIntent = mNameToIntent.get(appName); + AppLaunchRunnable runnable = new AppLaunchRunnable(startIntent); + Thread t = new Thread(runnable); + long startTime = System.currentTimeMillis(); + t.start(); + try { + t.join(JOIN_TIMEOUT); + } catch (InterruptedException e) { + // ignore + } + if(t.isAlive() || (runnable.getResult() != null && + runnable.getResult().result != ActivityManager.START_SUCCESS)) { + Log.w(TAG, "Assuming app " + appName + " crashed."); + reportError(appName, mNameToProcess.get(appName), results); + return; + } + long startUpTime = System.currentTimeMillis() - startTime; + results.putString(mNameToResultKey.get(appName), String.valueOf(startUpTime)); + sleep(5000); + } + + private void closeApp() { + Intent homeIntent = new Intent(Intent.ACTION_MAIN); + homeIntent.addCategory(Intent.CATEGORY_HOME); + homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + getInstrumentation().getContext().startActivity(homeIntent); + sleep(3000); + } + + private void sleep(int time) { + try { + Thread.sleep(time); + } catch (InterruptedException e) { + // ignore + } + } + + private void reportError(String appName, String processName, Bundle results) { + ActivityManager am = (ActivityManager) getInstrumentation() + .getContext().getSystemService(Context.ACTIVITY_SERVICE); + List<ProcessErrorStateInfo> crashes = am.getProcessesInErrorState(); + if (crashes != null) { + for (ProcessErrorStateInfo crash : crashes) { + if (!crash.processName.equals(processName)) + continue; + + Log.w(TAG, appName + " crashed: " + crash.shortMsg); + results.putString(mNameToResultKey.get(appName), crash.shortMsg); + return; + } + } + + results.putString(mNameToResultKey.get(appName), + "Crashed for unknown reason"); + Log.w(TAG, appName + + " not found in process list, most likely it is crashed"); + } + + private class AppLaunchRunnable implements Runnable { + private Intent mLaunchIntent; + private IActivityManager.WaitResult mResult; + public AppLaunchRunnable(Intent intent) { + mLaunchIntent = intent; + } + + public IActivityManager.WaitResult getResult() { + return mResult; + } + + public void run() { + try { + String mimeType = mLaunchIntent.getType(); + if (mimeType == null && mLaunchIntent.getData() != null + && "content".equals(mLaunchIntent.getData().getScheme())) { + mimeType = mAm.getProviderMimeType(mLaunchIntent.getData(), + UserHandle.USER_CURRENT); + } + + mResult = mAm.startActivityAndWait(null, mLaunchIntent, mimeType, + null, null, 0, mLaunchIntent.getFlags(), null, null, null, + UserHandle.USER_CURRENT); + } catch (RemoteException e) { + Log.w(TAG, "Error launching app", e); + } + } + } +} diff --git a/tests/MemoryUsage/Android.mk b/tests/MemoryUsage/Android.mk index e7bfb4f45c6f..0ab793b0e0c4 100644 --- a/tests/MemoryUsage/Android.mk +++ b/tests/MemoryUsage/Android.mk @@ -8,7 +8,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_PACKAGE_NAME := MemoryUsage -LOCAL_SDK_VERSION := 7 +LOCAL_CERTIFICATE := platform +LOCAL_JAVA_LIBRARIES := android.test.runner include $(BUILD_PACKAGE) diff --git a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java index 5e27ba791e36..b550957de63a 100644 --- a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java +++ b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java @@ -18,14 +18,17 @@ package com.android.tests.memoryusage; import android.app.ActivityManager; import android.app.ActivityManager.ProcessErrorStateInfo; import android.app.ActivityManager.RunningAppProcessInfo; +import android.app.ActivityManagerNative; +import android.app.IActivityManager; import android.content.Context; import android.content.Intent; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.os.Bundle; import android.os.Debug.MemoryInfo; +import android.os.RemoteException; +import android.os.UserHandle; import android.test.InstrumentationTestCase; import android.util.Log; @@ -48,8 +51,9 @@ public class MemoryUsageTest extends InstrumentationTestCase { private static final int SLEEP_TIME = 1000; private static final int THRESHOLD = 1024; - private static final int MAX_ITERATIONS = 10; - private static final int MIN_ITERATIONS = 4; + private static final int MAX_ITERATIONS = 20; + private static final int MIN_ITERATIONS = 6; + private static final int JOIN_TIMEOUT = 10000; private static final String TAG = "MemoryUsageInstrumentation"; private static final String KEY_APPS = "apps"; @@ -58,10 +62,13 @@ public class MemoryUsageTest extends InstrumentationTestCase { private Map<String, String> mNameToProcess; private Map<String, String> mNameToResultKey; + private IActivityManager mAm; + public void testMemory() { MemoryUsageInstrumentation instrumentation = - (MemoryUsageInstrumentation) getInstrumentation(); + (MemoryUsageInstrumentation) getInstrumentation(); Bundle args = instrumentation.getBundle(); + mAm = ActivityManagerNative.getDefault(); createMappings(); parseArgs(args); @@ -136,7 +143,16 @@ public class MemoryUsageTest extends InstrumentationTestCase { String process = mNameToProcess.get(appName); Intent startIntent = mNameToIntent.get(appName); - getInstrumentation().getContext().startActivity(startIntent); + + AppLaunchRunnable runnable = new AppLaunchRunnable(startIntent); + Thread t = new Thread(runnable); + t.start(); + try { + t.join(JOIN_TIMEOUT); + } catch (InterruptedException e) { + // ignore + } + return process; } @@ -234,7 +250,7 @@ public class MemoryUsageTest extends InstrumentationTestCase { } int[] pids = { - proc.pid }; + proc.pid }; MemoryInfo meminfo = am.getProcessMemoryInfo(pids)[0]; return meminfo.getTotalPss(); @@ -242,4 +258,29 @@ public class MemoryUsageTest extends InstrumentationTestCase { } return -1; } + + private class AppLaunchRunnable implements Runnable { + private Intent mLaunchIntent; + + public AppLaunchRunnable(Intent intent) { + mLaunchIntent = intent; + } + + public void run() { + try { + String mimeType = mLaunchIntent.getType(); + if (mimeType == null && mLaunchIntent.getData() != null + && "content".equals(mLaunchIntent.getData().getScheme())) { + mimeType = mAm.getProviderMimeType(mLaunchIntent.getData(), + UserHandle.USER_CURRENT); + } + + mAm.startActivityAndWait(null, mLaunchIntent, mimeType, + null, null, 0, mLaunchIntent.getFlags(), null, null, null, + UserHandle.USER_CURRENT_OR_SELF); + } catch (RemoteException e) { + Log.w(TAG, "Error launching app", e); + } + } + } } |