Merge "Don't allow the shell to change admin-locked app permissions."
diff --git a/apct-tests/perftests/core/Android.mk b/apct-tests/perftests/core/Android.mk
index eb07c05..5fe2f02 100644
--- a/apct-tests/perftests/core/Android.mk
+++ b/apct-tests/perftests/core/Android.mk
@@ -12,5 +12,8 @@
LOCAL_PACKAGE_NAME := CorePerfTests
+# Use google-fonts/dancing-script for the performance metrics
+LOCAL_ASSET_DIR := $(TOP)/external/google-fonts/dancing-script
+
include $(BUILD_PACKAGE)
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java
new file mode 100644
index 0000000..11ee599
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2016 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.graphics.perftests;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.graphics.Typeface;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class TypefaceCreatePerfTest {
+ // A font file name in asset directory.
+ private static final String TEST_FONT_NAME = "DancingScript-Regular.ttf";
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Test
+ public void testCreate_fromFamily() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ while (state.keepRunning()) {
+ Typeface face = Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL);
+ }
+ }
+
+ @Test
+ public void testCreate_fromFamilyName() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ while (state.keepRunning()) {
+ Typeface face = Typeface.create("monospace", Typeface.NORMAL);
+ }
+ }
+
+ @Test
+ public void testCreate_fromAsset() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final Context context = InstrumentationRegistry.getContext();
+ final AssetManager am = context.getAssets();
+
+ while (state.keepRunning()) {
+ Typeface face = Typeface.createFromAsset(am, TEST_FONT_NAME);
+ }
+ }
+
+ @Test
+ public void testCreate_fromFile() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final Context context = InstrumentationRegistry.getContext();
+ final AssetManager am = context.getAssets();
+
+ File outFile = null;
+ try {
+ outFile = File.createTempFile("example", "ttf", context.getCacheDir());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ try (InputStream in = am.open(TEST_FONT_NAME);
+ OutputStream out = new FileOutputStream(outFile)) {
+ byte[] buf = new byte[1024];
+ int n = 0;
+ while ((n = in.read(buf)) != -1) {
+ out.write(buf, 0, n);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ while (state.keepRunning()) {
+ Typeface face = Typeface.createFromFile(outFile);
+ }
+
+ outFile.delete();
+ }
+}
diff --git a/api/current.txt b/api/current.txt
index 3d11602..0a8965f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -21462,6 +21462,7 @@
field public static final int AMR_NB = 3; // 0x3
field public static final int AMR_WB = 4; // 0x4
field public static final int DEFAULT = 0; // 0x0
+ field public static final int MPEG_2_TS = 8; // 0x8
field public static final int MPEG_4 = 2; // 0x2
field public static final deprecated int RAW_AMR = 3; // 0x3
field public static final int THREE_GPP = 1; // 0x1
@@ -30306,6 +30307,8 @@
public final class PrintJobInfo implements android.os.Parcelable {
method public int describeContents();
+ method public int getAdvancedIntOption(java.lang.String);
+ method public java.lang.String getAdvancedStringOption(java.lang.String);
method public android.print.PrintAttributes getAttributes();
method public int getCopies();
method public long getCreationTime();
@@ -30314,6 +30317,7 @@
method public android.print.PageRange[] getPages();
method public android.print.PrinterId getPrinterId();
method public int getState();
+ method public boolean hasAdvancedOption(java.lang.String);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.print.PrintJobInfo> CREATOR;
field public static final int STATE_BLOCKED = 4; // 0x4
@@ -45845,6 +45849,8 @@
method public android.webkit.WebSettings getSettings();
method public java.lang.String getTitle();
method public java.lang.String getUrl();
+ method public android.webkit.WebChromeClient getWebChromeClient();
+ method public android.webkit.WebViewClient getWebViewClient();
method public void goBack();
method public void goBackOrForward(int);
method public void goForward();
@@ -53280,8 +53286,6 @@
method public java.net.URLConnection openConnection(java.net.Proxy) throws java.io.IOException;
method public final java.io.InputStream openStream() throws java.io.IOException;
method public boolean sameFile(java.net.URL);
- method protected void set(java.lang.String, java.lang.String, int, java.lang.String, java.lang.String);
- method protected void set(java.lang.String, java.lang.String, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
method public static void setURLStreamHandlerFactory(java.net.URLStreamHandlerFactory);
method public java.lang.String toExternalForm();
method public java.net.URI toURI() throws java.net.URISyntaxException;
@@ -59424,8 +59428,10 @@
method public java.util.Set<java.lang.String> getUnicodeLocaleKeys();
method public java.lang.String getUnicodeLocaleType(java.lang.String);
method public java.lang.String getVariant();
+ method public boolean hasExtensions();
method public static synchronized void setDefault(java.util.Locale);
method public static synchronized void setDefault(java.util.Locale.Category, java.util.Locale);
+ method public java.util.Locale stripExtensions();
method public java.lang.String toLanguageTag();
method public final java.lang.String toString();
field public static final java.util.Locale CANADA;
diff --git a/api/system-current.txt b/api/system-current.txt
index 1ab4884..2b5c33d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -22994,6 +22994,7 @@
field public static final int AMR_NB = 3; // 0x3
field public static final int AMR_WB = 4; // 0x4
field public static final int DEFAULT = 0; // 0x0
+ field public static final int MPEG_2_TS = 8; // 0x8
field public static final int MPEG_4 = 2; // 0x2
field public static final deprecated int RAW_AMR = 3; // 0x3
field public static final int THREE_GPP = 1; // 0x1
@@ -32906,6 +32907,8 @@
public final class PrintJobInfo implements android.os.Parcelable {
method public int describeContents();
+ method public int getAdvancedIntOption(java.lang.String);
+ method public java.lang.String getAdvancedStringOption(java.lang.String);
method public android.print.PrintAttributes getAttributes();
method public int getCopies();
method public long getCreationTime();
@@ -32914,6 +32917,7 @@
method public android.print.PageRange[] getPages();
method public android.print.PrinterId getPrinterId();
method public int getState();
+ method public boolean hasAdvancedOption(java.lang.String);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.print.PrintJobInfo> CREATOR;
field public static final int STATE_BLOCKED = 4; // 0x4
@@ -49121,6 +49125,8 @@
method public android.webkit.WebSettings getSettings();
method public java.lang.String getTitle();
method public java.lang.String getUrl();
+ method public android.webkit.WebChromeClient getWebChromeClient();
+ method public android.webkit.WebViewClient getWebViewClient();
method public android.webkit.WebViewProvider getWebViewProvider();
method public void goBack();
method public void goBackOrForward(int);
@@ -49401,6 +49407,8 @@
method public abstract java.lang.String getUrl();
method public abstract android.webkit.WebViewProvider.ViewDelegate getViewDelegate();
method public abstract int getVisibleTitleHeight();
+ method public abstract android.webkit.WebChromeClient getWebChromeClient();
+ method public abstract android.webkit.WebViewClient getWebViewClient();
method public abstract android.view.View getZoomControls();
method public abstract void goBack();
method public abstract void goBackOrForward(int);
@@ -56817,8 +56825,6 @@
method public java.net.URLConnection openConnection(java.net.Proxy) throws java.io.IOException;
method public final java.io.InputStream openStream() throws java.io.IOException;
method public boolean sameFile(java.net.URL);
- method protected void set(java.lang.String, java.lang.String, int, java.lang.String, java.lang.String);
- method protected void set(java.lang.String, java.lang.String, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
method public static void setURLStreamHandlerFactory(java.net.URLStreamHandlerFactory);
method public java.lang.String toExternalForm();
method public java.net.URI toURI() throws java.net.URISyntaxException;
@@ -62961,8 +62967,10 @@
method public java.util.Set<java.lang.String> getUnicodeLocaleKeys();
method public java.lang.String getUnicodeLocaleType(java.lang.String);
method public java.lang.String getVariant();
+ method public boolean hasExtensions();
method public static synchronized void setDefault(java.util.Locale);
method public static synchronized void setDefault(java.util.Locale.Category, java.util.Locale);
+ method public java.util.Locale stripExtensions();
method public java.lang.String toLanguageTag();
method public final java.lang.String toString();
field public static final java.util.Locale CANADA;
diff --git a/api/test-current.txt b/api/test-current.txt
index 6d4590d..4de2c61 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -21535,6 +21535,7 @@
field public static final int AMR_NB = 3; // 0x3
field public static final int AMR_WB = 4; // 0x4
field public static final int DEFAULT = 0; // 0x0
+ field public static final int MPEG_2_TS = 8; // 0x8
field public static final int MPEG_4 = 2; // 0x2
field public static final deprecated int RAW_AMR = 3; // 0x3
field public static final int THREE_GPP = 1; // 0x1
@@ -30381,6 +30382,8 @@
public final class PrintJobInfo implements android.os.Parcelable {
method public int describeContents();
+ method public int getAdvancedIntOption(java.lang.String);
+ method public java.lang.String getAdvancedStringOption(java.lang.String);
method public android.print.PrintAttributes getAttributes();
method public int getCopies();
method public long getCreationTime();
@@ -30391,6 +30394,7 @@
method public float getProgress();
method public int getState();
method public java.lang.CharSequence getStatus(android.content.pm.PackageManager);
+ method public boolean hasAdvancedOption(java.lang.String);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.print.PrintJobInfo> CREATOR;
field public static final int STATE_BLOCKED = 4; // 0x4
@@ -45933,6 +45937,8 @@
method public android.webkit.WebSettings getSettings();
method public java.lang.String getTitle();
method public java.lang.String getUrl();
+ method public android.webkit.WebChromeClient getWebChromeClient();
+ method public android.webkit.WebViewClient getWebViewClient();
method public void goBack();
method public void goBackOrForward(int);
method public void goForward();
@@ -53382,8 +53388,6 @@
method public java.net.URLConnection openConnection(java.net.Proxy) throws java.io.IOException;
method public final java.io.InputStream openStream() throws java.io.IOException;
method public boolean sameFile(java.net.URL);
- method protected void set(java.lang.String, java.lang.String, int, java.lang.String, java.lang.String);
- method protected void set(java.lang.String, java.lang.String, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
method public static void setURLStreamHandlerFactory(java.net.URLStreamHandlerFactory);
method public java.lang.String toExternalForm();
method public java.net.URI toURI() throws java.net.URISyntaxException;
@@ -59526,8 +59530,10 @@
method public java.util.Set<java.lang.String> getUnicodeLocaleKeys();
method public java.lang.String getUnicodeLocaleType(java.lang.String);
method public java.lang.String getVariant();
+ method public boolean hasExtensions();
method public static synchronized void setDefault(java.util.Locale);
method public static synchronized void setDefault(java.util.Locale.Category, java.util.Locale);
+ method public java.util.Locale stripExtensions();
method public java.lang.String toLanguageTag();
method public final java.lang.String toString();
field public static final java.util.Locale CANADA;
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index f798512..d91472b 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1229,8 +1229,9 @@
case UPDATE_CONFIGURATION_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
Configuration config = Configuration.CREATOR.createFromParcel(data);
- updateConfiguration(config);
+ final boolean updated = updateConfiguration(config);
reply.writeNoException();
+ reply.writeInt(updated ? 1 : 0);
return true;
}
@@ -4593,7 +4594,7 @@
data.recycle();
return res;
}
- public void updateConfiguration(Configuration values) throws RemoteException
+ public boolean updateConfiguration(Configuration values) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -4601,8 +4602,10 @@
values.writeToParcel(data, 0);
mRemote.transact(UPDATE_CONFIGURATION_TRANSACTION, data, reply, 0);
reply.readException();
+ boolean updated = reply.readInt() == 1;
data.recycle();
reply.recycle();
+ return updated;
}
public void setRequestedOrientation(IBinder token, int requestedOrientation)
throws RemoteException {
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 096f0cb..c9d3682 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -276,8 +276,9 @@
* Updates global configuration and applies changes to the entire system.
* @param values Update values for global configuration.
* @throws RemoteException
+ * @return Returns true if the configuration was updated.
*/
- public void updateConfiguration(Configuration values) throws RemoteException;
+ public boolean updateConfiguration(Configuration values) throws RemoteException;
public void setRequestedOrientation(IBinder token,
int requestedOrientation) throws RemoteException;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 74f48c6..02c8f0c 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3037,6 +3037,18 @@
"android.intent.action.MANAGED_PROFILE_UNAVAILABLE";
/**
+ * Broadcast sent to the system user when the 'device locked' state changes for any user.
+ * Carries an extra {@link #EXTRA_USER_HANDLE} that specifies the ID of the user for which
+ * the device was locked or unlocked.
+ *
+ * This is only sent to registered receivers.
+ *
+ * @hide
+ */
+ public static final String ACTION_DEVICE_LOCKED_CHANGED =
+ "android.intent.action.DEVICE_LOCKED_CHANGED";
+
+ /**
* Sent when the user taps on the clock widget in the system's "quick settings" area.
*/
public static final String ACTION_QUICK_CLOCK =
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index f6e6ad6..85f999b 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -46,6 +46,7 @@
import java.util.Map;
import java.util.Set;
+import dalvik.annotation.optimization.FastNative;
import dalvik.system.VMRuntime;
/**
@@ -254,22 +255,35 @@
// see libbinder's binder/Status.h
private static final int EX_TRANSACTION_FAILED = -129;
+ @FastNative
private static native int nativeDataSize(long nativePtr);
+ @FastNative
private static native int nativeDataAvail(long nativePtr);
+ @FastNative
private static native int nativeDataPosition(long nativePtr);
+ @FastNative
private static native int nativeDataCapacity(long nativePtr);
+ @FastNative
private static native long nativeSetDataSize(long nativePtr, int size);
+ @FastNative
private static native void nativeSetDataPosition(long nativePtr, int pos);
+ @FastNative
private static native void nativeSetDataCapacity(long nativePtr, int size);
+ @FastNative
private static native boolean nativePushAllowFds(long nativePtr, boolean allowFds);
+ @FastNative
private static native void nativeRestoreAllowFds(long nativePtr, boolean lastValue);
private static native void nativeWriteByteArray(long nativePtr, byte[] b, int offset, int len);
private static native void nativeWriteBlob(long nativePtr, byte[] b, int offset, int len);
+ @FastNative
private static native void nativeWriteInt(long nativePtr, int val);
+ @FastNative
private static native void nativeWriteLong(long nativePtr, long val);
+ @FastNative
private static native void nativeWriteFloat(long nativePtr, float val);
+ @FastNative
private static native void nativeWriteDouble(long nativePtr, double val);
private static native void nativeWriteString(long nativePtr, String val);
private static native void nativeWriteStrongBinder(long nativePtr, IBinder val);
@@ -277,9 +291,13 @@
private static native byte[] nativeCreateByteArray(long nativePtr);
private static native byte[] nativeReadBlob(long nativePtr);
+ @FastNative
private static native int nativeReadInt(long nativePtr);
+ @FastNative
private static native long nativeReadLong(long nativePtr);
+ @FastNative
private static native float nativeReadFloat(long nativePtr);
+ @FastNative
private static native double nativeReadDouble(long nativePtr);
private static native String nativeReadString(long nativePtr);
private static native IBinder nativeReadStrongBinder(long nativePtr);
@@ -294,6 +312,7 @@
long nativePtr, byte[] data, int offset, int length);
private static native long nativeAppendFrom(
long thisNativePtr, long otherNativePtr, int offset, int length);
+ @FastNative
private static native boolean nativeHasFileDescriptors(long nativePtr);
private static native void nativeWriteInterfaceToken(long nativePtr, String interfaceName);
private static native void nativeEnforceInterface(long nativePtr, String interfaceName);
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index 672df6d..b3d76d7 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -20,6 +20,8 @@
import android.content.Context;
import android.util.Slog;
+import dalvik.annotation.optimization.CriticalNative;
+
/**
* Core timekeeping facilities.
*
@@ -124,7 +126,7 @@
}
duration = start + ms - uptimeMillis();
} while (duration > 0);
-
+
if (interrupted) {
// Important: we don't want to quietly eat an interrupt() event,
// so we make sure to re-interrupt the thread so that the next
@@ -132,7 +134,7 @@
Thread.currentThread().interrupt();
}
}
-
+
/**
* Sets the current wall time, in milliseconds. Requires the calling
* process to have appropriate permissions.
@@ -162,6 +164,7 @@
*
* @return milliseconds of non-sleep uptime since boot.
*/
+ @CriticalNative
native public static long uptimeMillis();
/**
@@ -169,6 +172,7 @@
*
* @return elapsed milliseconds since boot.
*/
+ @CriticalNative
native public static long elapsedRealtime();
/**
@@ -176,30 +180,34 @@
*
* @return elapsed nanoseconds since boot.
*/
+ @CriticalNative
public static native long elapsedRealtimeNanos();
/**
* Returns milliseconds running in the current thread.
- *
+ *
* @return elapsed milliseconds in the thread
*/
+ @CriticalNative
public static native long currentThreadTimeMillis();
/**
* Returns microseconds running in the current thread.
- *
+ *
* @return elapsed microseconds in the thread
- *
+ *
* @hide
*/
+ @CriticalNative
public static native long currentThreadTimeMicro();
/**
* Returns current wall time in microseconds.
- *
+ *
* @return elapsed microseconds in wall time
- *
+ *
* @hide
*/
+ @CriticalNative
public static native long currentTimeMicro();
}
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 3ae28fd..7e8cc0b8 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -16,6 +16,8 @@
package android.os;
+import dalvik.annotation.optimization.FastNative;
+
/**
* Writes trace events to the system trace buffer. These trace events can be
* collected and visualized using the Systrace tool.
@@ -91,14 +93,20 @@
private static volatile long sEnabledTags = TRACE_TAG_NOT_READY;
private static native long nativeGetEnabledTags();
- private static native void nativeTraceCounter(long tag, String name, int value);
- private static native void nativeTraceBegin(long tag, String name);
- private static native void nativeTraceEnd(long tag);
- private static native void nativeAsyncTraceBegin(long tag, String name, int cookie);
- private static native void nativeAsyncTraceEnd(long tag, String name, int cookie);
private static native void nativeSetAppTracingAllowed(boolean allowed);
private static native void nativeSetTracingEnabled(boolean allowed);
+ @FastNative
+ private static native void nativeTraceCounter(long tag, String name, int value);
+ @FastNative
+ private static native void nativeTraceBegin(long tag, String name);
+ @FastNative
+ private static native void nativeTraceEnd(long tag);
+ @FastNative
+ private static native void nativeAsyncTraceBegin(long tag, String name, int cookie);
+ @FastNative
+ private static native void nativeAsyncTraceEnd(long tag, String name, int cookie);
+
static {
// We configure two separate change callbacks, one in Trace.cpp and one here. The
// native callback reads the tags from the system property, and this callback
diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java
index f134943..3d094f7 100644
--- a/core/java/android/print/PrintJobInfo.java
+++ b/core/java/android/print/PrintJobInfo.java
@@ -586,8 +586,6 @@
*
* @param key The option key.
* @return Whether the option is present.
- *
- * @hide
*/
public boolean hasAdvancedOption(String key) {
return mAdvancedOptions != null && mAdvancedOptions.containsKey(key);
@@ -598,8 +596,6 @@
*
* @param key The option key.
* @return The option value.
- *
- * @hide
*/
public String getAdvancedStringOption(String key) {
if (mAdvancedOptions != null) {
@@ -613,8 +609,6 @@
*
* @param key The option key.
* @return The option value.
- *
- * @hide
*/
public int getAdvancedIntOption(String key) {
if (mAdvancedOptions != null) {
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 6ddaf3b..98371f4 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -36,8 +36,10 @@
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.OperationCanceledException;
+import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor.OnCloseListener;
+import android.os.Parcelable;
import android.os.RemoteException;
import android.os.storage.StorageVolume;
import android.system.ErrnoException;
@@ -644,6 +646,8 @@
public static final String METHOD_REMOVE_DOCUMENT = "android:removeDocument";
/** {@hide} */
public static final String METHOD_EJECT_ROOT = "android:ejectRoot";
+ /** {@hide} */
+ public static final String METHOD_FIND_PATH = "android:findPath";
/** {@hide} */
public static final String EXTRA_PARENT_URI = "parentUri";
@@ -1307,6 +1311,41 @@
}
/**
+ * Finds the canonical path to the root. Document id should be unique across
+ * roots.
+ *
+ * @param documentUri uri of the document which path is requested.
+ * @return the path to the root of the document, or {@code null} if failed.
+ * @see DocumentsProvider#findPath(String)
+ *
+ * {@hide}
+ */
+ public static Path findPath(ContentResolver resolver, Uri documentUri)
+ throws RemoteException {
+ final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
+ documentUri.getAuthority());
+ try {
+ return findPath(client, documentUri);
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to find path", e);
+ return null;
+ } finally {
+ ContentProviderClient.releaseQuietly(client);
+ }
+ }
+
+ /** {@hide} */
+ public static Path findPath(ContentProviderClient client, Uri documentUri)
+ throws RemoteException {
+ final Bundle in = new Bundle();
+ in.putParcelable(DocumentsContract.EXTRA_URI, documentUri);
+
+ final Bundle out = client.call(METHOD_FIND_PATH, null, in);
+
+ return out.getParcelable(DocumentsContract.EXTRA_RESULT);
+ }
+
+ /**
* Open the given image for thumbnail purposes, using any embedded EXIF
* thumbnail if available, and providing orientation hints from the parent
* image.
@@ -1345,4 +1384,51 @@
return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH, extras);
}
+
+ /**
+ * Holds a path from a root to a particular document under it.
+ *
+ * @hide
+ */
+ public static final class Path implements Parcelable {
+
+ public final String mRootId;
+ public final List<String> mPath;
+
+ /**
+ * Creates a Path.
+ * @param rootId the id of the root
+ * @param path the list of document ids from the root document
+ * at position 0 to the target document
+ */
+ public Path(String rootId, List<String> path) {
+ mRootId = rootId;
+ mPath = path;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mRootId);
+ dest.writeStringList(mPath);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<Path> CREATOR = new Creator<Path>() {
+ @Override
+ public Path createFromParcel(Parcel in) {
+ final String rootId = in.readString();
+ final List<String> path = in.createStringArrayList();
+ return new Path(rootId, path);
+ }
+
+ @Override
+ public Path[] newArray(int size) {
+ return new Path[size];
+ }
+ };
+ }
}
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 6117ce4..6234f6a 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -20,6 +20,7 @@
import static android.provider.DocumentsContract.METHOD_CREATE_DOCUMENT;
import static android.provider.DocumentsContract.METHOD_DELETE_DOCUMENT;
import static android.provider.DocumentsContract.METHOD_EJECT_ROOT;
+import static android.provider.DocumentsContract.METHOD_FIND_PATH;
import static android.provider.DocumentsContract.METHOD_IS_CHILD_DOCUMENT;
import static android.provider.DocumentsContract.METHOD_MOVE_DOCUMENT;
import static android.provider.DocumentsContract.METHOD_REMOVE_DOCUMENT;
@@ -33,6 +34,7 @@
import static android.provider.DocumentsContract.getTreeDocumentId;
import static android.provider.DocumentsContract.isTreeUri;
+import android.Manifest;
import android.annotation.CallSuper;
import android.content.ClipDescription;
import android.content.ContentProvider;
@@ -53,6 +55,7 @@
import android.os.ParcelFileDescriptor.OnCloseListener;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
+import android.provider.DocumentsContract.Path;
import android.util.Log;
import libcore.io.IoUtils;
@@ -323,6 +326,26 @@
}
/**
+ * Finds the canonical path to the root for the requested document. If there are
+ * more than one path to this document, return the most typical one.
+ *
+ * <p>This API assumes that document id has enough info to infer the root.
+ * Different roots should use different document id to refer to the same
+ * document.
+ *
+ * @param documentId the document which path is requested.
+ * @return the path of the requested document to the root, or null if
+ * such operation is not supported.
+ *
+ * @hide
+ */
+ public Path findPath(String documentId)
+ throws FileNotFoundException {
+ Log.w(TAG, "findPath is called on an unsupported provider.");
+ return null;
+ }
+
+ /**
* Return all roots currently provided. To display to users, you must define
* at least one root. You should avoid making network requests to keep this
* request fast.
@@ -873,6 +896,12 @@
// It's responsibility of the provider to revoke any grants, as the document may be
// still attached to another parents.
+ } else if (METHOD_FIND_PATH.equals(method)) {
+ getContext().enforceCallingPermission(Manifest.permission.MANAGE_DOCUMENTS, null);
+
+ final Path path = findPath(documentId);
+
+ out.putParcelable(DocumentsContract.EXTRA_RESULT, path);
} else {
throw new UnsupportedOperationException("Method not supported " + method);
}
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index 5a9a1ea..4393992 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -16,6 +16,7 @@
package android.view;
+import dalvik.annotation.optimization.FastNative;
import dalvik.system.CloseGuard;
import android.os.Looper;
@@ -47,6 +48,7 @@
private static native long nativeInit(WeakReference<DisplayEventReceiver> receiver,
MessageQueue messageQueue);
private static native void nativeDispose(long receiverPtr);
+ @FastNative
private static native void nativeScheduleVsync(long receiverPtr);
/**
diff --git a/core/java/android/view/DragEvent.java b/core/java/android/view/DragEvent.java
index 2baa0b4..16f2d7d 100644
--- a/core/java/android/view/DragEvent.java
+++ b/core/java/android/view/DragEvent.java
@@ -26,7 +26,7 @@
//TODO: Improve Javadoc
/**
* Represents an event that is sent out by the system at various times during a drag and drop
- * operation. It is a complex data structure that contains several important pieces of data about
+ * operation. It is a data structure that contains several important pieces of data about
* the operation and the underlying data.
* <p>
* View objects that receive a DragEvent call {@link #getAction()}, which returns
@@ -161,8 +161,8 @@
* event when they are added or becoming visible.
* </p>
* <p>
- * A View only receives further drag events if it returns {@code true} in response to
- * ACTION_DRAG_STARTED.
+ * A View only receives further drag events for the drag operation if it returns {@code true}
+ * in response to ACTION_DRAG_STARTED.
* </p>
* @see #ACTION_DRAG_ENDED
* @see #getX()
@@ -172,8 +172,9 @@
/**
* Action constant returned by {@link #getAction()}: Sent to a View after
- * {@link #ACTION_DRAG_ENTERED} if the drag shadow is still within the View object's bounding
- * box. The {@link #getX()} and {@link #getY()} methods supply
+ * {@link #ACTION_DRAG_ENTERED} while the drag shadow is still within the View object's bounding
+ * box, but not within a descendant view that can accept the data. The {@link #getX()} and
+ * {@link #getY()} methods supply
* the X and Y position of of the drag point within the View object's bounding box.
* <p>
* A View receives an {@link #ACTION_DRAG_ENTERED} event before receiving any
@@ -355,9 +356,10 @@
/**
* Returns the {@link android.content.ClipData} object sent to the system as part of the call
* to
- * {@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()}.
+ * {@link android.view.View#startDragAndDrop(ClipData,View.DragShadowBuilder,Object,int)
+ * startDragAndDrop()}.
* This method only returns valid data if the event action is {@link #ACTION_DROP}.
- * @return The ClipData sent to the system by startDrag().
+ * @return The ClipData sent to the system by startDragAndDrop().
*/
public ClipData getClipData() {
return mClipData;
@@ -366,12 +368,14 @@
/**
* Returns the {@link android.content.ClipDescription} object contained in the
* {@link android.content.ClipData} object sent to the system as part of the call to
- * {@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()}.
+ * {@link android.view.View#startDragAndDrop(ClipData,View.DragShadowBuilder,Object,int)
+ * startDragAndDrop()}.
* The drag handler or listener for a View can use the metadata in this object to decide if the
* View can accept the dragged View object's data.
* <p>
* This method returns valid data for all event actions except for {@link #ACTION_DRAG_ENDED}.
- * @return The ClipDescription that was part of the ClipData sent to the system by startDrag().
+ * @return The ClipDescription that was part of the ClipData sent to the system by
+ * startDragAndDrop().
*/
public ClipDescription getClipDescription() {
return mClipDescription;
@@ -384,7 +388,8 @@
/**
* Returns the local state object sent to the system as part of the call to
- * {@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()}.
+ * {@link android.view.View#startDragAndDrop(ClipData,View.DragShadowBuilder,Object,int)
+ * startDragAndDrop()}.
* The object is intended to provide local information about the drag and drop operation. For
* example, it can indicate whether the drag and drop operation is a copy or a move.
* <p>
@@ -394,7 +399,7 @@
* <p>
* This method returns valid data for all event actions except for {@link #ACTION_DRAG_ENDED}.
* </p>
- * @return The local state object sent to the system by startDrag().
+ * @return The local state object sent to the system by startDragAndDrop().
*/
public Object getLocalState() {
return mLocalState;
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index fab5364..3e8d577 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -23,6 +23,9 @@
import android.os.SystemClock;
import android.util.SparseArray;
+import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
+
/**
* Object used to report movement (mouse, pen, finger, trackball) events.
* Motion events may hold either absolute or relative movements and other data,
@@ -1445,60 +1448,98 @@
float xOffset, float yOffset, float xPrecision, float yPrecision,
long downTimeNanos, long eventTimeNanos,
int pointerCount, PointerProperties[] pointerIds, PointerCoords[] pointerCoords);
- private static native long nativeCopy(long destNativePtr, long sourceNativePtr,
- boolean keepHistory);
private static native void nativeDispose(long nativePtr);
private static native void nativeAddBatch(long nativePtr, long eventTimeNanos,
PointerCoords[] pointerCoords, int metaState);
-
- private static native int nativeGetDeviceId(long nativePtr);
- private static native int nativeGetSource(long nativePtr);
- private static native int nativeSetSource(long nativePtr, int source);
- private static native int nativeGetAction(long nativePtr);
- private static native void nativeSetAction(long nativePtr, int action);
- private static native boolean nativeIsTouchEvent(long nativePtr);
- private static native int nativeGetFlags(long nativePtr);
- private static native void nativeSetFlags(long nativePtr, int flags);
- private static native int nativeGetEdgeFlags(long nativePtr);
- private static native void nativeSetEdgeFlags(long nativePtr, int action);
- private static native int nativeGetMetaState(long nativePtr);
- private static native int nativeGetButtonState(long nativePtr);
- private static native void nativeSetButtonState(long nativePtr, int buttonState);
- private static native int nativeGetActionButton(long nativePtr);
- private static native void nativeSetActionButton(long nativePtr, int actionButton);
- private static native void nativeOffsetLocation(long nativePtr, float deltaX, float deltaY);
- private static native float nativeGetXOffset(long nativePtr);
- private static native float nativeGetYOffset(long nativePtr);
- private static native float nativeGetXPrecision(long nativePtr);
- private static native float nativeGetYPrecision(long nativePtr);
- private static native long nativeGetDownTimeNanos(long nativePtr);
- private static native void nativeSetDownTimeNanos(long nativePtr, long downTime);
-
- private static native int nativeGetPointerCount(long nativePtr);
- private static native int nativeGetPointerId(long nativePtr, int pointerIndex);
- private static native int nativeGetToolType(long nativePtr, int pointerIndex);
- private static native int nativeFindPointerIndex(long nativePtr, int pointerId);
-
- private static native int nativeGetHistorySize(long nativePtr);
- private static native long nativeGetEventTimeNanos(long nativePtr, int historyPos);
- private static native float nativeGetRawAxisValue(long nativePtr,
- int axis, int pointerIndex, int historyPos);
- private static native float nativeGetAxisValue(long nativePtr,
- int axis, int pointerIndex, int historyPos);
private static native void nativeGetPointerCoords(long nativePtr,
int pointerIndex, int historyPos, PointerCoords outPointerCoords);
private static native void nativeGetPointerProperties(long nativePtr,
int pointerIndex, PointerProperties outPointerProperties);
- private static native void nativeScale(long nativePtr, float scale);
- private static native void nativeTransform(long nativePtr, Matrix matrix);
-
private static native long nativeReadFromParcel(long nativePtr, Parcel parcel);
private static native void nativeWriteToParcel(long nativePtr, Parcel parcel);
private static native String nativeAxisToString(int axis);
private static native int nativeAxisFromString(String label);
+ // -------------- @FastNative -------------------------
+
+ @FastNative
+ private static native int nativeGetPointerId(long nativePtr, int pointerIndex);
+ @FastNative
+ private static native int nativeGetToolType(long nativePtr, int pointerIndex);
+ @FastNative
+ private static native long nativeGetEventTimeNanos(long nativePtr, int historyPos);
+ @FastNative
+ private static native float nativeGetRawAxisValue(long nativePtr,
+ int axis, int pointerIndex, int historyPos);
+ @FastNative
+ private static native float nativeGetAxisValue(long nativePtr,
+ int axis, int pointerIndex, int historyPos);
+
+ // -------------- @CriticalNative ----------------------
+
+ @CriticalNative
+ private static native long nativeCopy(long destNativePtr, long sourceNativePtr,
+ boolean keepHistory);
+ @CriticalNative
+ private static native int nativeGetDeviceId(long nativePtr);
+ @CriticalNative
+ private static native int nativeGetSource(long nativePtr);
+ @CriticalNative
+ private static native int nativeSetSource(long nativePtr, int source);
+ @CriticalNative
+ private static native int nativeGetAction(long nativePtr);
+ @CriticalNative
+ private static native void nativeSetAction(long nativePtr, int action);
+ @CriticalNative
+ private static native boolean nativeIsTouchEvent(long nativePtr);
+ @CriticalNative
+ private static native int nativeGetFlags(long nativePtr);
+ @CriticalNative
+ private static native void nativeSetFlags(long nativePtr, int flags);
+ @CriticalNative
+ private static native int nativeGetEdgeFlags(long nativePtr);
+ @CriticalNative
+ private static native void nativeSetEdgeFlags(long nativePtr, int action);
+ @CriticalNative
+ private static native int nativeGetMetaState(long nativePtr);
+ @CriticalNative
+ private static native int nativeGetButtonState(long nativePtr);
+ @CriticalNative
+ private static native void nativeSetButtonState(long nativePtr, int buttonState);
+ @CriticalNative
+ private static native int nativeGetActionButton(long nativePtr);
+ @CriticalNative
+ private static native void nativeSetActionButton(long nativePtr, int actionButton);
+ @CriticalNative
+ private static native void nativeOffsetLocation(long nativePtr, float deltaX, float deltaY);
+ @CriticalNative
+ private static native float nativeGetXOffset(long nativePtr);
+ @CriticalNative
+ private static native float nativeGetYOffset(long nativePtr);
+ @CriticalNative
+ private static native float nativeGetXPrecision(long nativePtr);
+ @CriticalNative
+ private static native float nativeGetYPrecision(long nativePtr);
+ @CriticalNative
+ private static native long nativeGetDownTimeNanos(long nativePtr);
+ @CriticalNative
+ private static native void nativeSetDownTimeNanos(long nativePtr, long downTime);
+
+ @CriticalNative
+ private static native int nativeGetPointerCount(long nativePtr);
+ @CriticalNative
+ private static native int nativeFindPointerIndex(long nativePtr, int pointerId);
+
+ @CriticalNative
+ private static native int nativeGetHistorySize(long nativePtr);
+
+ @CriticalNative
+ private static native void nativeScale(long nativePtr, float scale);
+ @CriticalNative
+ private static native void nativeTransform(long nativePtr, long matrix);
+
private MotionEvent() {
}
@@ -2065,7 +2106,7 @@
public final int getPointerCount() {
return nativeGetPointerCount(mNativePtr);
}
-
+
/**
* Return the pointer identifier associated with a particular pointer
* data index in this event. The identifier tells you the actual pointer
@@ -2891,7 +2932,7 @@
throw new IllegalArgumentException("matrix must not be null");
}
- nativeTransform(mNativePtr, matrix);
+ nativeTransform(mNativePtr, matrix.native_instance);
}
/**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d060aac..bdf93e4 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -20661,7 +20661,9 @@
return false;
}
- data.prepareToLeaveProcess((flags & View.DRAG_FLAG_GLOBAL) != 0);
+ if (data != null) {
+ data.prepareToLeaveProcess((flags & View.DRAG_FLAG_GLOBAL) != 0);
+ }
boolean okay = false;
@@ -20830,6 +20832,9 @@
* {@link android.view.DragEvent#ACTION_DROP} if it consumed the drop, or
* {@code false} if it didn't.
* </p>
+ * <p>
+ * For all other events, the return value is ignored.
+ * </p>
*/
public boolean onDragEvent(DragEvent event) {
return false;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 745a82b..87b330d 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -5552,8 +5552,8 @@
// Remember who the current drag target is pre-dispatch
final View prevDragView = mCurrentDragView;
- if (what == DragEvent.ACTION_DROP) {
- event.getClipData().prepareToEnterProcess();
+ if (what == DragEvent.ACTION_DROP && event.mClipData != null) {
+ event.mClipData.prepareToEnterProcess();
}
// Now dispatch the drag/drop event
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 44f6fac..c125b32 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -92,9 +92,6 @@
/** @hide */
public static final int AUTOCLICK_DELAY_DEFAULT = 600;
- /** @hide */
- public static final int MAX_A11Y_EVENTS_PER_SERVICE_CALL = 20;
-
static final Object sInstanceSync = new Object();
private static AccessibilityManager sInstance;
@@ -103,8 +100,6 @@
private IAccessibilityManager mService;
- private EventDispatchThread mEventDispatchThread;
-
final int mUserId;
final Handler mHandler;
@@ -303,32 +298,44 @@
* their descendants.
*/
public void sendAccessibilityEvent(AccessibilityEvent event) {
- if (!isEnabled()) {
- Looper myLooper = Looper.myLooper();
- if (myLooper == Looper.getMainLooper()) {
- throw new IllegalStateException(
- "Accessibility off. Did you forget to check that?");
- } else {
- // If we're not running on the thread with the main looper, it's possible for
- // the state of accessibility to change between checking isEnabled and
- // calling this method. So just log the error rather than throwing the
- // exception.
- Log.e(LOG_TAG, "AccessibilityEvent sent with accessibility disabled");
+ final IAccessibilityManager service;
+ final int userId;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
return;
}
- }
- event.setEventTime(SystemClock.uptimeMillis());
-
- getEventDispatchThread().scheduleEvent(event);
- }
-
- private EventDispatchThread getEventDispatchThread() {
- synchronized (mLock) {
- if (mEventDispatchThread == null) {
- mEventDispatchThread = new EventDispatchThread(mService, mUserId);
- mEventDispatchThread.start();
+ if (!mIsEnabled) {
+ Looper myLooper = Looper.myLooper();
+ if (myLooper == Looper.getMainLooper()) {
+ throw new IllegalStateException(
+ "Accessibility off. Did you forget to check that?");
+ } else {
+ // If we're not running on the thread with the main looper, it's possible for
+ // the state of accessibility to change between checking isEnabled and
+ // calling this method. So just log the error rather than throwing the
+ // exception.
+ Log.e(LOG_TAG, "AccessibilityEvent sent with accessibility disabled");
+ return;
+ }
}
- return mEventDispatchThread;
+ userId = mUserId;
+ }
+ try {
+ event.setEventTime(SystemClock.uptimeMillis());
+ // it is possible that this manager is in the same process as the service but
+ // client using it is called through Binder from another process. Example: MMS
+ // app adds a SMS notification and the NotificationManagerService calls this method
+ long identityToken = Binder.clearCallingIdentity();
+ service.sendAccessibilityEvent(event, userId);
+ Binder.restoreCallingIdentity(identityToken);
+ if (DEBUG) {
+ Log.i(LOG_TAG, event + " sent");
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error during sending " + event + " ", re);
+ } finally {
+ event.recycle();
}
}
@@ -713,99 +720,4 @@
}
}
}
-
- private static class EventDispatchThread extends Thread {
- // Second lock used to keep UI thread performant. Never try to grab mLock when holding
- // this one, or the UI thread will block in send AccessibilityEvent.
- private final Object mEventQueueLock = new Object();
-
- // Two lists to hold events. The app thread fills one while we empty the other.
- private final ArrayList<AccessibilityEvent> mEventLists0 =
- new ArrayList<>(MAX_A11Y_EVENTS_PER_SERVICE_CALL);
- private final ArrayList<AccessibilityEvent> mEventLists1 =
- new ArrayList<>(MAX_A11Y_EVENTS_PER_SERVICE_CALL);
-
- private boolean mPingPongListToggle;
-
- private final IAccessibilityManager mService;
-
- private final int mUserId;
-
- EventDispatchThread(IAccessibilityManager service, int userId) {
- mService = service;
- mUserId = userId;
- }
-
- @Override
- public void run() {
- while (true) {
- ArrayList<AccessibilityEvent> listBeingDrained;
- synchronized (mEventQueueLock) {
- ArrayList<AccessibilityEvent> listBeingFilled = getListBeingFilledLocked();
- if (listBeingFilled.isEmpty()) {
- try {
- mEventQueueLock.wait();
- } catch (InterruptedException e) {
- // Treat as a notify
- }
- }
- // Swap buffers
- mPingPongListToggle = !mPingPongListToggle;
- listBeingDrained = listBeingFilled;
- }
- dispatchEvents(listBeingDrained);
- }
- }
-
- public void scheduleEvent(AccessibilityEvent event) {
- synchronized (mEventQueueLock) {
- getListBeingFilledLocked().add(event);
- mEventQueueLock.notifyAll();
- }
- }
-
- private ArrayList<AccessibilityEvent> getListBeingFilledLocked() {
- return (mPingPongListToggle) ? mEventLists0 : mEventLists1;
- }
-
- private void dispatchEvents(ArrayList<AccessibilityEvent> events) {
- int eventListCapacityLowerBound = events.size();
- while (events.size() > 0) {
- // We don't want to consume extra memory if an app sends a lot of events in a
- // one-off event. Cap the list length at double the max events per call.
- // We'll end up with extra GC for apps that send huge numbers of events, but
- // sending that many events will lead to bad performance in any case.
- if ((eventListCapacityLowerBound > 2 * MAX_A11Y_EVENTS_PER_SERVICE_CALL)
- && (events.size() <= 2 * MAX_A11Y_EVENTS_PER_SERVICE_CALL)) {
- events.trimToSize();
- eventListCapacityLowerBound = events.size();
- }
- // We only expect this loop to run once, as the app shouldn't be sending
- // huge numbers of events.
- // The clear in the called method will remove the sent events
- dispatchOneBatchOfEvents(events.subList(0,
- Math.min(events.size(), MAX_A11Y_EVENTS_PER_SERVICE_CALL)));
- }
- }
-
- private void dispatchOneBatchOfEvents(List<AccessibilityEvent> events) {
- if (events.isEmpty()) {
- return;
- }
- long identityToken = Binder.clearCallingIdentity();
- try {
- mService.sendAccessibilityEvents(new ParceledListSlice<>(events),
- mUserId);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error sending multiple events");
- }
- Binder.restoreCallingIdentity(identityToken);
- if (DEBUG) {
- Log.i(LOG_TAG, events.size() + " events sent");
- }
- for (int i = events.size() - 1; i >= 0; i--) {
- events.remove(i).recycle();
- }
- }
- }
}
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index aa9cb39..71e77c4 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -21,7 +21,6 @@
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.content.ComponentName;
-import android.content.pm.ParceledListSlice;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
@@ -30,7 +29,7 @@
/**
* Interface implemented by the AccessibilityManagerService called by
- * the AccessibilityMasngers.
+ * the AccessibilityManagers.
*
* @hide
*/
@@ -40,8 +39,6 @@
void sendAccessibilityEvent(in AccessibilityEvent uiEvent, int userId);
- void sendAccessibilityEvents(in ParceledListSlice events, int userId);
-
List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(int userId);
List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType, int userId);
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index aaa7b26..8c725cf 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1768,6 +1768,7 @@
* requests. This will replace the current handler.
*
* @param client an implementation of WebViewClient
+ * @see #getWebViewClient
*/
public void setWebViewClient(WebViewClient client) {
checkThread();
@@ -1775,6 +1776,17 @@
}
/**
+ * Gets the WebViewClient.
+ *
+ * @return the WebViewClient, or a default client if not yet set
+ * @see #setWebViewClient
+ */
+ public WebViewClient getWebViewClient() {
+ checkThread();
+ return mProvider.getWebViewClient();
+ }
+
+ /**
* Registers the interface to be used when content can not be handled by
* the rendering engine, and should be downloaded instead. This will replace
* the current handler.
@@ -1792,6 +1804,7 @@
* This will replace the current handler.
*
* @param client an implementation of WebChromeClient
+ * @see #getWebChromeClient
*/
public void setWebChromeClient(WebChromeClient client) {
checkThread();
@@ -1799,6 +1812,17 @@
}
/**
+ * Gets the chrome handler.
+ *
+ * @return the WebChromeClient, or null if not yet set
+ * @see #setWebChromeClient
+ */
+ public WebChromeClient getWebChromeClient() {
+ checkThread();
+ return mProvider.getWebChromeClient();
+ }
+
+ /**
* Sets the Picture listener. This is an interface used to receive
* notifications of a new Picture.
*
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index e5b65e7..95ec179 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -229,10 +229,14 @@
public void setWebViewClient(WebViewClient client);
+ public WebViewClient getWebViewClient();
+
public void setDownloadListener(DownloadListener listener);
public void setWebChromeClient(WebChromeClient client);
+ public WebChromeClient getWebChromeClient();
+
public void setPictureListener(PictureListener listener);
public void addJavascriptInterface(Object obj, String interfaceName);
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index 278ceb2..5003c61 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -132,7 +132,7 @@
private CharSequence mDescFormat;
- private boolean mAttached;
+ private boolean mRegistered;
private Calendar mTime;
private String mTimeZone;
@@ -252,7 +252,7 @@
}
createTime(mTimeZone);
- // Wait until onAttachedToWindow() to handle the ticker
+ // Wait until registering for events to handle the ticker
chooseFormat(false);
}
@@ -503,7 +503,7 @@
boolean hadSeconds = mHasSeconds;
mHasSeconds = DateFormat.hasSeconds(mFormat);
- if (handleTicker && mAttached && hadSeconds != mHasSeconds) {
+ if (handleTicker && mRegistered && hadSeconds != mHasSeconds) {
if (hadSeconds) getHandler().removeCallbacks(mTicker);
else mTicker.run();
}
@@ -517,11 +517,9 @@
}
@Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
-
- if (!mAttached) {
- mAttached = true;
+ public void onVisibilityAggregated(boolean isVisible) {
+ if (!mRegistered && isVisible) {
+ mRegistered = true;
registerReceiver();
registerObserver();
@@ -533,20 +531,13 @@
} else {
onTimeChanged();
}
- }
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
-
- if (mAttached) {
+ } else if (mRegistered && !isVisible) {
unregisterReceiver();
unregisterObserver();
getHandler().removeCallbacks(mTicker);
- mAttached = false;
+ mRegistered = false;
}
}
@@ -569,7 +560,7 @@
}
private void registerObserver() {
- if (isAttachedToWindow()) {
+ if (mRegistered) {
if (mFormatChangeObserver == null) {
mFormatChangeObserver = new FormatChangeObserver(getHandler());
}
diff --git a/core/jni/android/graphics/Matrix.cpp b/core/jni/android/graphics/Matrix.cpp
index b0f3bb4..f8bb77a 100644
--- a/core/jni/android/graphics/Matrix.cpp
+++ b/core/jni/android/graphics/Matrix.cpp
@@ -2,16 +2,16 @@
**
** Copyright 2006, 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
+** 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
+** 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
+** 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.
*/
@@ -25,14 +25,25 @@
namespace android {
+static_assert(sizeof(SkMatrix) == 40, "Unexpected sizeof(SkMatrix), "
+ "update size in Matrix.java#NATIVE_ALLOCATION_SIZE and here");
+static_assert(SK_SCALAR_IS_FLOAT, "SK_SCALAR_IS_FLOAT is false, "
+ "only float scalar is supported");
+
class SkMatrixGlue {
public:
- static void finalizer(JNIEnv* env, jobject clazz, jlong objHandle) {
+ // ---------------- Regular JNI -----------------------------
+
+ static void finalizer(jlong objHandle) {
SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
delete obj;
}
+ static jlong getNativeFinalizer(JNIEnv* env, jobject clazz) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&finalizer));
+ }
+
static jlong create(JNIEnv* env, jobject clazz, jlong srcHandle) {
const SkMatrix* src = reinterpret_cast<SkMatrix*>(srcHandle);
SkMatrix* obj = new SkMatrix();
@@ -43,156 +54,39 @@
return reinterpret_cast<jlong>(obj);
}
- static jboolean isIdentity(JNIEnv* env, jobject clazz, jlong objHandle) {
- SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
- return obj->isIdentity() ? JNI_TRUE : JNI_FALSE;
- }
+ // ---------------- @FastNative -----------------------------
- static jboolean isAffine(JNIEnv* env, jobject clazz, jlong objHandle) {
- SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
- return obj->asAffine(NULL) ? JNI_TRUE : JNI_FALSE;
- }
-
- static jboolean rectStaysRect(JNIEnv* env, jobject clazz, jlong objHandle) {
- SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
- return obj->rectStaysRect() ? JNI_TRUE : JNI_FALSE;
- }
-
- static void reset(JNIEnv* env, jobject clazz, jlong objHandle) {
- SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
- obj->reset();
- }
- static void set(JNIEnv* env, jobject clazz, jlong objHandle, jlong otherHandle) {
- SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
- SkMatrix* other = reinterpret_cast<SkMatrix*>(otherHandle);
- *obj = *other;
- }
- static void setTranslate(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) {
- SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
- obj->setTranslate(dx, dy);
- }
- static void setScale__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sx, jfloat sy, jfloat px, jfloat py) {
- SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
- obj->setScale(sx, sy, px, py);
- }
- static void setScale__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sx, jfloat sy) {
- SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
- obj->setScale(sx, sy);
- }
- static void setRotate__FFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat degrees, jfloat px, jfloat py) {
- SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
- obj->setRotate(degrees, px, py);
- }
- static void setRotate__F(JNIEnv* env, jobject clazz, jlong objHandle, jfloat degrees) {
- SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
- obj->setRotate(degrees);
- }
- static void setSinCos__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sinValue, jfloat cosValue, jfloat px, jfloat py) {
- SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
- obj->setSinCos(sinValue, cosValue, px, py);
- }
- static void setSinCos__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sinValue, jfloat cosValue) {
- SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
- obj->setSinCos(sinValue, cosValue);
- }
- static void setSkew__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat kx, jfloat ky, jfloat px, jfloat py) {
- SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
- obj->setSkew(kx, ky, px, py);
- }
- static void setSkew__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat kx, jfloat ky) {
- SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
- obj->setSkew(kx, ky);
- }
- static void setConcat(JNIEnv* env, jobject clazz, jlong objHandle, jlong aHandle, jlong bHandle) {
- SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
- SkMatrix* a = reinterpret_cast<SkMatrix*>(aHandle);
- SkMatrix* b = reinterpret_cast<SkMatrix*>(bHandle);
- obj->setConcat(*a, *b);
- }
-
- static void preTranslate(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) {
- SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
- obj->preTranslate(dx, dy);
- }
-
- static void preScale__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sx, jfloat sy, jfloat px, jfloat py) {
- SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
- obj->preScale(sx, sy, px, py);
- }
-
- static void preScale__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sx, jfloat sy) {
- SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
- obj->preScale(sx, sy);
- }
-
- static void preRotate__FFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat degrees, jfloat px, jfloat py) {
- SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
- obj->preRotate(degrees, px, py);
- }
-
- static void preRotate__F(JNIEnv* env, jobject clazz, jlong objHandle, jfloat degrees) {
- SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
- obj->preRotate(degrees);
- }
-
- static void preSkew__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat kx, jfloat ky, jfloat px, jfloat py) {
- SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
- obj->preSkew(kx, ky, px, py);
- }
-
- static void preSkew__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat kx, jfloat ky) {
- SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
- obj->preSkew(kx, ky);
- }
-
- static void preConcat(JNIEnv* env, jobject clazz, jlong objHandle, jlong otherHandle) {
- SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
- SkMatrix* other = reinterpret_cast<SkMatrix*>(otherHandle);
- obj->preConcat(*other);
- }
-
- static void postTranslate(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) {
- SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
- obj->postTranslate(dx, dy);
- }
-
- static void postScale__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sx, jfloat sy, jfloat px, jfloat py) {
- SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
- obj->postScale(sx, sy, px, py);
- }
-
- static void postScale__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sx, jfloat sy) {
- SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
- obj->postScale(sx, sy);
- }
-
- static void postRotate__FFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat degrees, jfloat px, jfloat py) {
- SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
- obj->postRotate(degrees, px, py);
- }
-
- static void postRotate__F(JNIEnv* env, jobject clazz, jlong objHandle, jfloat degrees) {
- SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
- obj->postRotate(degrees);
- }
-
- static void postSkew__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat kx, jfloat ky, jfloat px, jfloat py) {
- SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
- obj->postSkew(kx, ky, px, py);
- }
-
- static void postSkew__FF(JNIEnv* env, jobject clazz, jlong matrixHandle, jfloat kx, jfloat ky) {
+ static void mapPoints(JNIEnv* env, jobject clazz, jlong matrixHandle,
+ jfloatArray dst, jint dstIndex, jfloatArray src, jint srcIndex,
+ jint ptCount, jboolean isPts) {
SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
- matrix->postSkew(kx, ky);
+ SkASSERT(ptCount >= 0);
+ AutoJavaFloatArray autoSrc(env, src, srcIndex + (ptCount << 1),
+ kRO_JNIAccess);
+ AutoJavaFloatArray autoDst(env, dst, dstIndex + (ptCount << 1),
+ kRW_JNIAccess);
+ float* srcArray = autoSrc.ptr() + srcIndex;
+ float* dstArray = autoDst.ptr() + dstIndex;
+ if (isPts)
+ matrix->mapPoints((SkPoint*) dstArray, (const SkPoint*) srcArray,
+ ptCount);
+ else
+ matrix->mapVectors((SkVector*) dstArray, (const SkVector*) srcArray,
+ ptCount);
}
- static void postConcat(JNIEnv* env, jobject clazz, jlong matrixHandle, jlong otherHandle) {
+ static jboolean mapRect__RectFRectF(JNIEnv* env, jobject clazz,
+ jlong matrixHandle, jobjectArray dst, jobject src) {
SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
- SkMatrix* other = reinterpret_cast<SkMatrix*>(otherHandle);
- matrix->postConcat(*other);
+ SkRect dst_, src_;
+ GraphicsJNI::jrectf_to_rect(env, src, &src_);
+ jboolean rectStaysRect = matrix->mapRect(&dst_, src_);
+ GraphicsJNI::rect_to_jrectf(dst_, env, dst);
+ return rectStaysRect ? JNI_TRUE : JNI_FALSE;
}
- static jboolean setRectToRect(JNIEnv* env, jobject clazz, jlong matrixHandle, jobject src, jobject dst, jint stfHandle) {
+ static jboolean setRectToRect(JNIEnv* env, jobject clazz,
+ jlong matrixHandle, jobject src, jobject dst, jint stfHandle) {
SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
SkMatrix::ScaleToFit stf = static_cast<SkMatrix::ScaleToFit>(stfHandle);
SkRect src_;
@@ -202,150 +96,290 @@
return matrix->setRectToRect(src_, dst_, stf) ? JNI_TRUE : JNI_FALSE;
}
- static jboolean setPolyToPoly(JNIEnv* env, jobject clazz, jlong matrixHandle,
- jfloatArray jsrc, jint srcIndex,
- jfloatArray jdst, jint dstIndex, jint ptCount) {
+ static jboolean setPolyToPoly(JNIEnv* env, jobject clazz,
+ jlong matrixHandle, jfloatArray jsrc, jint srcIndex,
+ jfloatArray jdst, jint dstIndex, jint ptCount) {
SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
SkASSERT(srcIndex >= 0);
SkASSERT(dstIndex >= 0);
- SkASSERT((unsigned)ptCount <= 4);
+ SkASSERT((unsigned )ptCount <= 4);
- AutoJavaFloatArray autoSrc(env, jsrc, srcIndex + (ptCount << 1), kRO_JNIAccess);
- AutoJavaFloatArray autoDst(env, jdst, dstIndex + (ptCount << 1), kRW_JNIAccess);
+ AutoJavaFloatArray autoSrc(env, jsrc, srcIndex + (ptCount << 1),
+ kRO_JNIAccess);
+ AutoJavaFloatArray autoDst(env, jdst, dstIndex + (ptCount << 1),
+ kRW_JNIAccess);
float* src = autoSrc.ptr() + srcIndex;
float* dst = autoDst.ptr() + dstIndex;
bool result;
-#ifdef SK_SCALAR_IS_FLOAT
- result = matrix->setPolyToPoly((const SkPoint*)src, (const SkPoint*)dst,
- ptCount);
-#else
- SkASSERT(false);
-#endif
+ result = matrix->setPolyToPoly((const SkPoint*) src,
+ (const SkPoint*) dst, ptCount);
return result ? JNI_TRUE : JNI_FALSE;
}
- static jboolean invert(JNIEnv* env, jobject clazz, jlong matrixHandle, jlong inverseHandle) {
+ static void getValues(JNIEnv* env, jobject clazz, jlong matrixHandle,
+ jfloatArray values) {
+ SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+ AutoJavaFloatArray autoValues(env, values, 9, kRW_JNIAccess);
+ float* dst = autoValues.ptr();
+ for (int i = 0; i < 9; i++) {
+ dst[i] = matrix->get(i);
+ }
+ }
+
+ static void setValues(JNIEnv* env, jobject clazz, jlong matrixHandle,
+ jfloatArray values) {
+ SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+ AutoJavaFloatArray autoValues(env, values, 9, kRO_JNIAccess);
+ const float* src = autoValues.ptr();
+
+ for (int i = 0; i < 9; i++) {
+ matrix->set(i, src[i]);
+ }
+ }
+
+ // ---------------- @CriticalNative -----------------------------
+
+ static jboolean isIdentity(jlong objHandle) {
+ SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+ return obj->isIdentity() ? JNI_TRUE : JNI_FALSE;
+ }
+
+ static jboolean isAffine(jlong objHandle) {
+ SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+ return obj->asAffine(NULL) ? JNI_TRUE : JNI_FALSE;
+ }
+
+ static jboolean rectStaysRect(jlong objHandle) {
+ SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+ return obj->rectStaysRect() ? JNI_TRUE : JNI_FALSE;
+ }
+
+ static void reset(jlong objHandle) {
+ SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+ obj->reset();
+ }
+
+ static void set(jlong objHandle, jlong otherHandle) {
+ SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+ SkMatrix* other = reinterpret_cast<SkMatrix*>(otherHandle);
+ *obj = *other;
+ }
+
+ static void setTranslate(jlong objHandle, jfloat dx, jfloat dy) {
+ SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+ obj->setTranslate(dx, dy);
+ }
+
+ static void setScale__FFFF(jlong objHandle, jfloat sx, jfloat sy, jfloat px,
+ jfloat py) {
+ SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+ obj->setScale(sx, sy, px, py);
+ }
+
+ static void setScale__FF(jlong objHandle, jfloat sx, jfloat sy) {
+ SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+ obj->setScale(sx, sy);
+ }
+
+ static void setRotate__FFF(jlong objHandle, jfloat degrees, jfloat px,
+ jfloat py) {
+ SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+ obj->setRotate(degrees, px, py);
+ }
+
+ static void setRotate__F(jlong objHandle, jfloat degrees) {
+ SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+ obj->setRotate(degrees);
+ }
+
+ static void setSinCos__FFFF(jlong objHandle, jfloat sinValue,
+ jfloat cosValue, jfloat px, jfloat py) {
+ SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+ obj->setSinCos(sinValue, cosValue, px, py);
+ }
+
+ static void setSinCos__FF(jlong objHandle, jfloat sinValue,
+ jfloat cosValue) {
+ SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+ obj->setSinCos(sinValue, cosValue);
+ }
+
+ static void setSkew__FFFF(jlong objHandle, jfloat kx, jfloat ky, jfloat px,
+ jfloat py) {
+ SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+ obj->setSkew(kx, ky, px, py);
+ }
+
+ static void setSkew__FF(jlong objHandle, jfloat kx, jfloat ky) {
+ SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+ obj->setSkew(kx, ky);
+ }
+
+ static void setConcat(jlong objHandle, jlong aHandle, jlong bHandle) {
+ SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+ SkMatrix* a = reinterpret_cast<SkMatrix*>(aHandle);
+ SkMatrix* b = reinterpret_cast<SkMatrix*>(bHandle);
+ obj->setConcat(*a, *b);
+ }
+
+ static void preTranslate(jlong objHandle, jfloat dx, jfloat dy) {
+ SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+ obj->preTranslate(dx, dy);
+ }
+
+ static void preScale__FFFF(jlong objHandle, jfloat sx, jfloat sy, jfloat px,
+ jfloat py) {
+ SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+ obj->preScale(sx, sy, px, py);
+ }
+
+ static void preScale__FF(jlong objHandle, jfloat sx, jfloat sy) {
+ SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+ obj->preScale(sx, sy);
+ }
+
+ static void preRotate__FFF(jlong objHandle, jfloat degrees, jfloat px,
+ jfloat py) {
+ SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+ obj->preRotate(degrees, px, py);
+ }
+
+ static void preRotate__F(jlong objHandle, jfloat degrees) {
+ SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+ obj->preRotate(degrees);
+ }
+
+ static void preSkew__FFFF(jlong objHandle, jfloat kx, jfloat ky, jfloat px,
+ jfloat py) {
+ SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+ obj->preSkew(kx, ky, px, py);
+ }
+
+ static void preSkew__FF(jlong objHandle, jfloat kx, jfloat ky) {
+ SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+ obj->preSkew(kx, ky);
+ }
+
+ static void preConcat(jlong objHandle, jlong otherHandle) {
+ SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+ SkMatrix* other = reinterpret_cast<SkMatrix*>(otherHandle);
+ obj->preConcat(*other);
+ }
+
+ static void postTranslate(jlong objHandle, jfloat dx, jfloat dy) {
+ SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+ obj->postTranslate(dx, dy);
+ }
+
+ static void postScale__FFFF(jlong objHandle, jfloat sx, jfloat sy,
+ jfloat px, jfloat py) {
+ SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+ obj->postScale(sx, sy, px, py);
+ }
+
+ static void postScale__FF(jlong objHandle, jfloat sx, jfloat sy) {
+ SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+ obj->postScale(sx, sy);
+ }
+
+ static void postRotate__FFF(jlong objHandle, jfloat degrees, jfloat px,
+ jfloat py) {
+ SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+ obj->postRotate(degrees, px, py);
+ }
+
+ static void postRotate__F(jlong objHandle, jfloat degrees) {
+ SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+ obj->postRotate(degrees);
+ }
+
+ static void postSkew__FFFF(jlong objHandle, jfloat kx, jfloat ky, jfloat px,
+ jfloat py) {
+ SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+ obj->postSkew(kx, ky, px, py);
+ }
+
+ static void postSkew__FF(jlong matrixHandle, jfloat kx, jfloat ky) {
+ SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+ matrix->postSkew(kx, ky);
+ }
+
+ static void postConcat(jlong matrixHandle, jlong otherHandle) {
+ SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+ SkMatrix* other = reinterpret_cast<SkMatrix*>(otherHandle);
+ matrix->postConcat(*other);
+ }
+
+ static jboolean invert(jlong matrixHandle, jlong inverseHandle) {
SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
SkMatrix* inverse = reinterpret_cast<SkMatrix*>(inverseHandle);
return matrix->invert(inverse);
}
- static void mapPoints(JNIEnv* env, jobject clazz, jlong matrixHandle,
- jfloatArray dst, jint dstIndex,
- jfloatArray src, jint srcIndex,
- jint ptCount, jboolean isPts) {
- SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
- SkASSERT(ptCount >= 0);
- AutoJavaFloatArray autoSrc(env, src, srcIndex + (ptCount << 1), kRO_JNIAccess);
- AutoJavaFloatArray autoDst(env, dst, dstIndex + (ptCount << 1), kRW_JNIAccess);
- float* srcArray = autoSrc.ptr() + srcIndex;
- float* dstArray = autoDst.ptr() + dstIndex;
-#ifdef SK_SCALAR_IS_FLOAT
- if (isPts)
- matrix->mapPoints((SkPoint*)dstArray, (const SkPoint*)srcArray,
- ptCount);
- else
- matrix->mapVectors((SkVector*)dstArray, (const SkVector*)srcArray,
- ptCount);
-#else
- SkASSERT(false);
-#endif
- }
-
- static jboolean mapRect__RectFRectF(JNIEnv* env, jobject clazz, jlong matrixHandle, jobjectArray dst, jobject src) {
- SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
- SkRect dst_, src_;
- GraphicsJNI::jrectf_to_rect(env, src, &src_);
- jboolean rectStaysRect = matrix->mapRect(&dst_, src_);
- GraphicsJNI::rect_to_jrectf(dst_, env, dst);
- return rectStaysRect ? JNI_TRUE : JNI_FALSE;
- }
-
- static jfloat mapRadius(JNIEnv* env, jobject clazz, jlong matrixHandle, jfloat radius) {
+ static jfloat mapRadius(jlong matrixHandle, jfloat radius) {
SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
float result;
result = SkScalarToFloat(matrix->mapRadius(radius));
return static_cast<jfloat>(result);
}
- static void getValues(JNIEnv* env, jobject clazz, jlong matrixHandle, jfloatArray values) {
- SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
- AutoJavaFloatArray autoValues(env, values, 9, kRW_JNIAccess);
- float* dst = autoValues.ptr();
-#ifdef SK_SCALAR_IS_FLOAT
- for (int i = 0; i < 9; i++) {
- dst[i] = matrix->get(i);
- }
-#else
- SkASSERT(false);
-#endif
- }
- static void setValues(JNIEnv* env, jobject clazz, jlong matrixHandle, jfloatArray values) {
- SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
- AutoJavaFloatArray autoValues(env, values, 9, kRO_JNIAccess);
- const float* src = autoValues.ptr();
-
-#ifdef SK_SCALAR_IS_FLOAT
- for (int i = 0; i < 9; i++) {
- matrix->set(i, src[i]);
- }
-#else
- SkASSERT(false);
-#endif
- }
-
- static jboolean equals(JNIEnv* env, jobject clazz, jlong aHandle, jlong bHandle) {
+ static jboolean equals(jlong aHandle, jlong bHandle) {
const SkMatrix* a = reinterpret_cast<SkMatrix*>(aHandle);
const SkMatrix* b = reinterpret_cast<SkMatrix*>(bHandle);
return *a == *b;
}
- };
+};
static const JNINativeMethod methods[] = {
- {"finalizer", "(J)V", (void*) SkMatrixGlue::finalizer},
- {"native_create","(J)J", (void*) SkMatrixGlue::create},
+ {"nGetNativeFinalizer", "()J", (void*) SkMatrixGlue::getNativeFinalizer},
+ {"nCreate","(J)J", (void*) SkMatrixGlue::create},
- {"native_isIdentity","!(J)Z", (void*) SkMatrixGlue::isIdentity},
- {"native_isAffine","!(J)Z", (void*) SkMatrixGlue::isAffine},
- {"native_rectStaysRect","!(J)Z", (void*) SkMatrixGlue::rectStaysRect},
- {"native_reset","!(J)V", (void*) SkMatrixGlue::reset},
- {"native_set","!(JJ)V", (void*) SkMatrixGlue::set},
- {"native_setTranslate","!(JFF)V", (void*) SkMatrixGlue::setTranslate},
- {"native_setScale","!(JFFFF)V", (void*) SkMatrixGlue::setScale__FFFF},
- {"native_setScale","!(JFF)V", (void*) SkMatrixGlue::setScale__FF},
- {"native_setRotate","!(JFFF)V", (void*) SkMatrixGlue::setRotate__FFF},
- {"native_setRotate","!(JF)V", (void*) SkMatrixGlue::setRotate__F},
- {"native_setSinCos","!(JFFFF)V", (void*) SkMatrixGlue::setSinCos__FFFF},
- {"native_setSinCos","!(JFF)V", (void*) SkMatrixGlue::setSinCos__FF},
- {"native_setSkew","!(JFFFF)V", (void*) SkMatrixGlue::setSkew__FFFF},
- {"native_setSkew","!(JFF)V", (void*) SkMatrixGlue::setSkew__FF},
- {"native_setConcat","!(JJJ)V", (void*) SkMatrixGlue::setConcat},
- {"native_preTranslate","!(JFF)V", (void*) SkMatrixGlue::preTranslate},
- {"native_preScale","!(JFFFF)V", (void*) SkMatrixGlue::preScale__FFFF},
- {"native_preScale","!(JFF)V", (void*) SkMatrixGlue::preScale__FF},
- {"native_preRotate","!(JFFF)V", (void*) SkMatrixGlue::preRotate__FFF},
- {"native_preRotate","!(JF)V", (void*) SkMatrixGlue::preRotate__F},
- {"native_preSkew","!(JFFFF)V", (void*) SkMatrixGlue::preSkew__FFFF},
- {"native_preSkew","!(JFF)V", (void*) SkMatrixGlue::preSkew__FF},
- {"native_preConcat","!(JJ)V", (void*) SkMatrixGlue::preConcat},
- {"native_postTranslate","!(JFF)V", (void*) SkMatrixGlue::postTranslate},
- {"native_postScale","!(JFFFF)V", (void*) SkMatrixGlue::postScale__FFFF},
- {"native_postScale","!(JFF)V", (void*) SkMatrixGlue::postScale__FF},
- {"native_postRotate","!(JFFF)V", (void*) SkMatrixGlue::postRotate__FFF},
- {"native_postRotate","!(JF)V", (void*) SkMatrixGlue::postRotate__F},
- {"native_postSkew","!(JFFFF)V", (void*) SkMatrixGlue::postSkew__FFFF},
- {"native_postSkew","!(JFF)V", (void*) SkMatrixGlue::postSkew__FF},
- {"native_postConcat","!(JJ)V", (void*) SkMatrixGlue::postConcat},
- {"native_setRectToRect","!(JLandroid/graphics/RectF;Landroid/graphics/RectF;I)Z", (void*) SkMatrixGlue::setRectToRect},
- {"native_setPolyToPoly","!(J[FI[FII)Z", (void*) SkMatrixGlue::setPolyToPoly},
- {"native_invert","!(JJ)Z", (void*) SkMatrixGlue::invert},
- {"native_mapPoints","!(J[FI[FIIZ)V", (void*) SkMatrixGlue::mapPoints},
- {"native_mapRect","!(JLandroid/graphics/RectF;Landroid/graphics/RectF;)Z", (void*) SkMatrixGlue::mapRect__RectFRectF},
- {"native_mapRadius","!(JF)F", (void*) SkMatrixGlue::mapRadius},
- {"native_getValues","!(J[F)V", (void*) SkMatrixGlue::getValues},
- {"native_setValues","!(J[F)V", (void*) SkMatrixGlue::setValues},
- {"native_equals", "!(JJ)Z", (void*) SkMatrixGlue::equals}
+ // ------- @FastNative below here ---------------
+ {"nMapPoints","(J[FI[FIIZ)V", (void*) SkMatrixGlue::mapPoints},
+ {"nMapRect","(JLandroid/graphics/RectF;Landroid/graphics/RectF;)Z",
+ (void*) SkMatrixGlue::mapRect__RectFRectF},
+ {"nSetRectToRect","(JLandroid/graphics/RectF;Landroid/graphics/RectF;I)Z",
+ (void*) SkMatrixGlue::setRectToRect},
+ {"nSetPolyToPoly","(J[FI[FII)Z", (void*) SkMatrixGlue::setPolyToPoly},
+ {"nGetValues","(J[F)V", (void*) SkMatrixGlue::getValues},
+ {"nSetValues","(J[F)V", (void*) SkMatrixGlue::setValues},
+
+ // ------- @CriticalNative below here ---------------
+ {"nIsIdentity","(J)Z", (void*) SkMatrixGlue::isIdentity},
+ {"nIsAffine","(J)Z", (void*) SkMatrixGlue::isAffine},
+ {"nRectStaysRect","(J)Z", (void*) SkMatrixGlue::rectStaysRect},
+ {"nReset","(J)V", (void*) SkMatrixGlue::reset},
+ {"nSet","(JJ)V", (void*) SkMatrixGlue::set},
+ {"nSetTranslate","(JFF)V", (void*) SkMatrixGlue::setTranslate},
+ {"nSetScale","(JFFFF)V", (void*) SkMatrixGlue::setScale__FFFF},
+ {"nSetScale","(JFF)V", (void*) SkMatrixGlue::setScale__FF},
+ {"nSetRotate","(JFFF)V", (void*) SkMatrixGlue::setRotate__FFF},
+ {"nSetRotate","(JF)V", (void*) SkMatrixGlue::setRotate__F},
+ {"nSetSinCos","(JFFFF)V", (void*) SkMatrixGlue::setSinCos__FFFF},
+ {"nSetSinCos","(JFF)V", (void*) SkMatrixGlue::setSinCos__FF},
+ {"nSetSkew","(JFFFF)V", (void*) SkMatrixGlue::setSkew__FFFF},
+ {"nSetSkew","(JFF)V", (void*) SkMatrixGlue::setSkew__FF},
+ {"nSetConcat","(JJJ)V", (void*) SkMatrixGlue::setConcat},
+ {"nPreTranslate","(JFF)V", (void*) SkMatrixGlue::preTranslate},
+ {"nPreScale","(JFFFF)V", (void*) SkMatrixGlue::preScale__FFFF},
+ {"nPreScale","(JFF)V", (void*) SkMatrixGlue::preScale__FF},
+ {"nPreRotate","(JFFF)V", (void*) SkMatrixGlue::preRotate__FFF},
+ {"nPreRotate","(JF)V", (void*) SkMatrixGlue::preRotate__F},
+ {"nPreSkew","(JFFFF)V", (void*) SkMatrixGlue::preSkew__FFFF},
+ {"nPreSkew","(JFF)V", (void*) SkMatrixGlue::preSkew__FF},
+ {"nPreConcat","(JJ)V", (void*) SkMatrixGlue::preConcat},
+ {"nPostTranslate","(JFF)V", (void*) SkMatrixGlue::postTranslate},
+ {"nPostScale","(JFFFF)V", (void*) SkMatrixGlue::postScale__FFFF},
+ {"nPostScale","(JFF)V", (void*) SkMatrixGlue::postScale__FF},
+ {"nPostRotate","(JFFF)V", (void*) SkMatrixGlue::postRotate__FFF},
+ {"nPostRotate","(JF)V", (void*) SkMatrixGlue::postRotate__F},
+ {"nPostSkew","(JFFFF)V", (void*) SkMatrixGlue::postSkew__FFFF},
+ {"nPostSkew","(JFF)V", (void*) SkMatrixGlue::postSkew__FF},
+ {"nPostConcat","(JJ)V", (void*) SkMatrixGlue::postConcat},
+ {"nInvert","(JJ)Z", (void*) SkMatrixGlue::invert},
+ {"nMapRadius","(JF)F", (void*) SkMatrixGlue::mapRadius},
+ {"nEquals", "(JJ)Z", (void*) SkMatrixGlue::equals}
};
static jfieldID sNativeInstanceField;
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 315dd6b..4b1530a 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -105,392 +105,80 @@
return reinterpret_cast<jlong>(obj);
}
- static void reset(JNIEnv* env, jobject clazz, jlong objHandle) {
- Paint* obj = reinterpret_cast<Paint*>(objHandle);
- obj->reset();
- defaultSettingsForAndroid(obj);
- }
+ static int breakText(JNIEnv* env, const Paint& paint, Typeface* typeface, const jchar text[],
+ int count, float maxWidth, jint bidiFlags, jfloatArray jmeasured,
+ const bool forwardScan) {
+ size_t measuredCount = 0;
+ float measured = 0;
- static void assign(JNIEnv* env, jobject clazz, jlong dstPaintHandle, jlong srcPaintHandle) {
- Paint* dst = reinterpret_cast<Paint*>(dstPaintHandle);
- const Paint* src = reinterpret_cast<Paint*>(srcPaintHandle);
- *dst = *src;
- }
+ std::unique_ptr<float[]> advancesArray(new float[count]);
+ MinikinUtils::measureText(&paint, bidiFlags, typeface, text, 0, count, count,
+ advancesArray.get());
- // Equivalent to the Java Paint's FILTER_BITMAP_FLAG.
- static const uint32_t sFilterBitmapFlag = 0x02;
-
- static jint getFlags(JNIEnv* env, jobject, jlong paintHandle) {
- Paint* nativePaint = reinterpret_cast<Paint*>(paintHandle);
- uint32_t result = nativePaint->getFlags();
- result &= ~sFilterBitmapFlag; // Filtering no longer stored in this bit. Mask away.
- if (nativePaint->getFilterQuality() != kNone_SkFilterQuality) {
- result |= sFilterBitmapFlag;
+ for (int i = 0; i < count; i++) {
+ // traverse in the given direction
+ int index = forwardScan ? i : (count - i - 1);
+ float width = advancesArray[index];
+ if (measured + width > maxWidth) {
+ break;
+ }
+ // properly handle clusters when scanning backwards
+ if (forwardScan || width != 0.0f) {
+ measuredCount = i + 1;
+ }
+ measured += width;
}
- return static_cast<jint>(result);
- }
- static void setFlags(JNIEnv* env, jobject, jlong paintHandle, jint flags) {
- Paint* nativePaint = reinterpret_cast<Paint*>(paintHandle);
- // Instead of modifying 0x02, change the filter level.
- nativePaint->setFilterQuality(flags & sFilterBitmapFlag
- ? kLow_SkFilterQuality
- : kNone_SkFilterQuality);
- // Don't pass through filter flag, which is no longer stored in paint's flags.
- flags &= ~sFilterBitmapFlag;
- // Use the existing value for 0x02.
- const uint32_t existing0x02Flag = nativePaint->getFlags() & sFilterBitmapFlag;
- flags |= existing0x02Flag;
- nativePaint->setFlags(flags);
- }
-
- static jint getHinting(JNIEnv* env, jobject, jlong paintHandle) {
- return reinterpret_cast<Paint*>(paintHandle)->getHinting()
- == Paint::kNo_Hinting ? 0 : 1;
- }
-
- static void setHinting(JNIEnv* env, jobject, jlong paintHandle, jint mode) {
- reinterpret_cast<Paint*>(paintHandle)->setHinting(
- mode == 0 ? Paint::kNo_Hinting : Paint::kNormal_Hinting);
- }
-
- static void setAntiAlias(JNIEnv* env, jobject, jlong paintHandle, jboolean aa) {
- reinterpret_cast<Paint*>(paintHandle)->setAntiAlias(aa);
- }
-
- static void setLinearText(JNIEnv* env, jobject, jlong paintHandle, jboolean linearText) {
- reinterpret_cast<Paint*>(paintHandle)->setLinearText(linearText);
- }
-
- static void setSubpixelText(JNIEnv* env, jobject, jlong paintHandle, jboolean subpixelText) {
- reinterpret_cast<Paint*>(paintHandle)->setSubpixelText(subpixelText);
- }
-
- static void setUnderlineText(JNIEnv* env, jobject, jlong paintHandle, jboolean underlineText) {
- reinterpret_cast<Paint*>(paintHandle)->setUnderlineText(underlineText);
- }
-
- static void setStrikeThruText(JNIEnv* env, jobject, jlong paintHandle, jboolean strikeThruText) {
- reinterpret_cast<Paint*>(paintHandle)->setStrikeThruText(strikeThruText);
- }
-
- static void setFakeBoldText(JNIEnv* env, jobject, jlong paintHandle, jboolean fakeBoldText) {
- reinterpret_cast<Paint*>(paintHandle)->setFakeBoldText(fakeBoldText);
- }
-
- static void setFilterBitmap(JNIEnv* env, jobject, jlong paintHandle, jboolean filterBitmap) {
- reinterpret_cast<Paint*>(paintHandle)->setFilterQuality(
- filterBitmap ? kLow_SkFilterQuality : kNone_SkFilterQuality);
- }
-
- static void setDither(JNIEnv* env, jobject, jlong paintHandle, jboolean dither) {
- reinterpret_cast<Paint*>(paintHandle)->setDither(dither);
- }
-
- static jint getStyle(JNIEnv* env, jobject clazz,jlong objHandle) {
- Paint* obj = reinterpret_cast<Paint*>(objHandle);
- return static_cast<jint>(obj->getStyle());
- }
-
- static void setStyle(JNIEnv* env, jobject clazz, jlong objHandle, jint styleHandle) {
- Paint* obj = reinterpret_cast<Paint*>(objHandle);
- Paint::Style style = static_cast<Paint::Style>(styleHandle);
- obj->setStyle(style);
- }
-
- static jint getColor(JNIEnv* env, jobject, jlong paintHandle) {
- int color;
- color = reinterpret_cast<Paint*>(paintHandle)->getColor();
- return static_cast<jint>(color);
- }
-
- static jint getAlpha(JNIEnv* env, jobject, jlong paintHandle) {
- int alpha;
- alpha = reinterpret_cast<Paint*>(paintHandle)->getAlpha();
- return static_cast<jint>(alpha);
- }
-
- static void setColor(JNIEnv* env, jobject, jlong paintHandle, jint color) {
- reinterpret_cast<Paint*>(paintHandle)->setColor(color);
- }
-
- static void setAlpha(JNIEnv* env, jobject, jlong paintHandle, jint a) {
- reinterpret_cast<Paint*>(paintHandle)->setAlpha(a);
- }
-
- static jfloat getStrokeWidth(JNIEnv* env, jobject, jlong paintHandle) {
- return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getStrokeWidth());
- }
-
- static void setStrokeWidth(JNIEnv* env, jobject, jlong paintHandle, jfloat width) {
- reinterpret_cast<Paint*>(paintHandle)->setStrokeWidth(width);
- }
-
- static jfloat getStrokeMiter(JNIEnv* env, jobject, jlong paintHandle) {
- return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getStrokeMiter());
- }
-
- static void setStrokeMiter(JNIEnv* env, jobject, jlong paintHandle, jfloat miter) {
- reinterpret_cast<Paint*>(paintHandle)->setStrokeMiter(miter);
- }
-
- static jint getStrokeCap(JNIEnv* env, jobject clazz, jlong objHandle) {
- Paint* obj = reinterpret_cast<Paint*>(objHandle);
- return static_cast<jint>(obj->getStrokeCap());
- }
-
- static void setStrokeCap(JNIEnv* env, jobject clazz, jlong objHandle, jint capHandle) {
- Paint* obj = reinterpret_cast<Paint*>(objHandle);
- Paint::Cap cap = static_cast<Paint::Cap>(capHandle);
- obj->setStrokeCap(cap);
- }
-
- static jint getStrokeJoin(JNIEnv* env, jobject clazz, jlong objHandle) {
- Paint* obj = reinterpret_cast<Paint*>(objHandle);
- return static_cast<jint>(obj->getStrokeJoin());
- }
-
- static void setStrokeJoin(JNIEnv* env, jobject clazz, jlong objHandle, jint joinHandle) {
- Paint* obj = reinterpret_cast<Paint*>(objHandle);
- Paint::Join join = (Paint::Join) joinHandle;
- obj->setStrokeJoin(join);
- }
-
- static jboolean getFillPath(JNIEnv* env, jobject clazz, jlong objHandle, jlong srcHandle, jlong dstHandle) {
- Paint* obj = reinterpret_cast<Paint*>(objHandle);
- SkPath* src = reinterpret_cast<SkPath*>(srcHandle);
- SkPath* dst = reinterpret_cast<SkPath*>(dstHandle);
- return obj->getFillPath(*src, dst) ? JNI_TRUE : JNI_FALSE;
- }
-
- static jlong setShader(JNIEnv* env, jobject clazz, jlong objHandle, jlong shaderHandle) {
- Paint* obj = reinterpret_cast<Paint*>(objHandle);
- SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
- return reinterpret_cast<jlong>(obj->setShader(shader));
- }
-
- static jlong setColorFilter(JNIEnv* env, jobject clazz, jlong objHandle, jlong filterHandle) {
- Paint* obj = reinterpret_cast<Paint *>(objHandle);
- SkColorFilter* filter = reinterpret_cast<SkColorFilter *>(filterHandle);
- return reinterpret_cast<jlong>(obj->setColorFilter(filter));
- }
-
- static void setXfermode(JNIEnv* env, jobject clazz, jlong paintHandle, jint xfermodeHandle) {
- // validate that the Java enum values match our expectations
- static_assert(0 == SkXfermode::kClear_Mode, "xfermode_mismatch");
- static_assert(1 == SkXfermode::kSrc_Mode, "xfermode_mismatch");
- static_assert(2 == SkXfermode::kDst_Mode, "xfermode_mismatch");
- static_assert(3 == SkXfermode::kSrcOver_Mode, "xfermode_mismatch");
- static_assert(4 == SkXfermode::kDstOver_Mode, "xfermode_mismatch");
- static_assert(5 == SkXfermode::kSrcIn_Mode, "xfermode_mismatch");
- static_assert(6 == SkXfermode::kDstIn_Mode, "xfermode_mismatch");
- static_assert(7 == SkXfermode::kSrcOut_Mode, "xfermode_mismatch");
- static_assert(8 == SkXfermode::kDstOut_Mode, "xfermode_mismatch");
- static_assert(9 == SkXfermode::kSrcATop_Mode, "xfermode_mismatch");
- static_assert(10 == SkXfermode::kDstATop_Mode, "xfermode_mismatch");
- static_assert(11 == SkXfermode::kXor_Mode, "xfermode_mismatch");
- static_assert(16 == SkXfermode::kDarken_Mode, "xfermode_mismatch");
- static_assert(17 == SkXfermode::kLighten_Mode, "xfermode_mismatch");
- static_assert(13 == SkXfermode::kModulate_Mode, "xfermode_mismatch");
- static_assert(14 == SkXfermode::kScreen_Mode, "xfermode_mismatch");
- static_assert(12 == SkXfermode::kPlus_Mode, "xfermode_mismatch");
- static_assert(15 == SkXfermode::kOverlay_Mode, "xfermode_mismatch");
-
- SkXfermode::Mode mode = static_cast<SkXfermode::Mode>(xfermodeHandle);
- Paint* paint = reinterpret_cast<Paint*>(paintHandle);
- paint->setXfermodeMode(mode);
- }
-
- static jlong setPathEffect(JNIEnv* env, jobject clazz, jlong objHandle, jlong effectHandle) {
- Paint* obj = reinterpret_cast<Paint*>(objHandle);
- SkPathEffect* effect = reinterpret_cast<SkPathEffect*>(effectHandle);
- return reinterpret_cast<jlong>(obj->setPathEffect(effect));
- }
-
- static jlong setMaskFilter(JNIEnv* env, jobject clazz, jlong objHandle, jlong maskfilterHandle) {
- Paint* obj = reinterpret_cast<Paint*>(objHandle);
- SkMaskFilter* maskfilter = reinterpret_cast<SkMaskFilter*>(maskfilterHandle);
- return reinterpret_cast<jlong>(obj->setMaskFilter(maskfilter));
- }
-
- static jlong setTypeface(JNIEnv* env, jobject clazz, jlong objHandle, jlong typefaceHandle) {
- // TODO: in Paint refactoring, set typeface on android Paint, not Paint
- return NULL;
- }
-
- static jlong setRasterizer(JNIEnv* env, jobject clazz, jlong objHandle, jlong rasterizerHandle) {
- Paint* obj = reinterpret_cast<Paint*>(objHandle);
- SkAutoTUnref<SkRasterizer> rasterizer(GraphicsJNI::refNativeRasterizer(rasterizerHandle));
- return reinterpret_cast<jlong>(obj->setRasterizer(rasterizer));
- }
-
- static jint getTextAlign(JNIEnv* env, jobject clazz, jlong objHandle) {
- Paint* obj = reinterpret_cast<Paint*>(objHandle);
- return static_cast<jint>(obj->getTextAlign());
- }
-
- static void setTextAlign(JNIEnv* env, jobject clazz, jlong objHandle, jint alignHandle) {
- Paint* obj = reinterpret_cast<Paint*>(objHandle);
- Paint::Align align = static_cast<Paint::Align>(alignHandle);
- obj->setTextAlign(align);
- }
-
- static jint setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) {
- Paint* obj = reinterpret_cast<Paint*>(objHandle);
- ScopedUtfChars localesChars(env, locales);
- jint minikinLangListId = minikin::FontStyle::registerLanguageList(localesChars.c_str());
- obj->setMinikinLangListId(minikinLangListId);
- return minikinLangListId;
- }
-
- static void setTextLocalesByMinikinLangListId(JNIEnv* env, jobject clazz, jlong objHandle,
- jint minikinLangListId) {
- Paint* obj = reinterpret_cast<Paint*>(objHandle);
- obj->setMinikinLangListId(minikinLangListId);
- }
-
- static jboolean isElegantTextHeight(JNIEnv* env, jobject, jlong paintHandle) {
- Paint* obj = reinterpret_cast<Paint*>(paintHandle);
- return obj->getFontVariant() == minikin::VARIANT_ELEGANT;
- }
-
- static void setElegantTextHeight(JNIEnv* env, jobject, jlong paintHandle, jboolean aa) {
- Paint* obj = reinterpret_cast<Paint*>(paintHandle);
- obj->setFontVariant(aa ? minikin::VARIANT_ELEGANT : minikin::VARIANT_DEFAULT);
- }
-
- static jfloat getTextSize(JNIEnv* env, jobject, jlong paintHandle) {
- return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getTextSize());
- }
-
- static void setTextSize(JNIEnv* env, jobject, jlong paintHandle, jfloat textSize) {
- reinterpret_cast<Paint*>(paintHandle)->setTextSize(textSize);
- }
-
- static jfloat getTextScaleX(JNIEnv* env, jobject, jlong paintHandle) {
- return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getTextScaleX());
- }
-
- static void setTextScaleX(JNIEnv* env, jobject, jlong paintHandle, jfloat scaleX) {
- reinterpret_cast<Paint*>(paintHandle)->setTextScaleX(scaleX);
- }
-
- static jfloat getTextSkewX(JNIEnv* env, jobject, jlong paintHandle) {
- return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getTextSkewX());
- }
-
- static void setTextSkewX(JNIEnv* env, jobject, jlong paintHandle, jfloat skewX) {
- reinterpret_cast<Paint*>(paintHandle)->setTextSkewX(skewX);
- }
-
- static jfloat getLetterSpacing(JNIEnv* env, jobject clazz, jlong paintHandle) {
- Paint* paint = reinterpret_cast<Paint*>(paintHandle);
- return paint->getLetterSpacing();
- }
-
- static void setLetterSpacing(JNIEnv* env, jobject clazz, jlong paintHandle, jfloat letterSpacing) {
- Paint* paint = reinterpret_cast<Paint*>(paintHandle);
- paint->setLetterSpacing(letterSpacing);
- }
-
- static void setFontFeatureSettings(JNIEnv* env, jobject clazz, jlong paintHandle, jstring settings) {
- Paint* paint = reinterpret_cast<Paint*>(paintHandle);
- if (!settings) {
- paint->setFontFeatureSettings(std::string());
- } else {
- ScopedUtfChars settingsChars(env, settings);
- paint->setFontFeatureSettings(std::string(settingsChars.c_str(), settingsChars.size()));
+ if (jmeasured && env->GetArrayLength(jmeasured) > 0) {
+ AutoJavaFloatArray autoMeasured(env, jmeasured, 1);
+ jfloat* array = autoMeasured.ptr();
+ array[0] = measured;
}
+ return measuredCount;
}
- static jint getHyphenEdit(JNIEnv* env, jobject clazz, jlong paintHandle, jint hyphen) {
- Paint* paint = reinterpret_cast<Paint*>(paintHandle);
- return paint->getHyphenEdit();
- }
+ static jint breakTextC(JNIEnv* env, jobject clazz, jlong paintHandle, jlong typefaceHandle,
+ jcharArray jtext, jint index, jint count, jfloat maxWidth, jint bidiFlags,
+ jfloatArray jmeasuredWidth) {
+ NPE_CHECK_RETURN_ZERO(env, jtext);
- static void setHyphenEdit(JNIEnv* env, jobject clazz, jlong paintHandle, jint hyphen) {
- Paint* paint = reinterpret_cast<Paint*>(paintHandle);
- paint->setHyphenEdit((uint32_t)hyphen);
- }
-
- static SkScalar getMetricsInternal(jlong paintHandle, jlong typefaceHandle,
- Paint::FontMetrics *metrics) {
- const int kElegantTop = 2500;
- const int kElegantBottom = -1000;
- const int kElegantAscent = 1900;
- const int kElegantDescent = -500;
- const int kElegantLeading = 0;
Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
- typeface = Typeface::resolveDefault(typeface);
- minikin::FakedFont baseFont = typeface->fFontCollection->baseFontFaked(typeface->fStyle);
- float saveSkewX = paint->getTextSkewX();
- bool savefakeBold = paint->isFakeBoldText();
- MinikinFontSkia::populateSkPaint(paint, baseFont.font, baseFont.fakery);
- SkScalar spacing = paint->getFontMetrics(metrics);
- // The populateSkPaint call may have changed fake bold / text skew
- // because we want to measure with those effects applied, so now
- // restore the original settings.
- paint->setTextSkewX(saveSkewX);
- paint->setFakeBoldText(savefakeBold);
- if (paint->getFontVariant() == minikin::VARIANT_ELEGANT) {
- SkScalar size = paint->getTextSize();
- metrics->fTop = -size * kElegantTop / 2048;
- metrics->fBottom = -size * kElegantBottom / 2048;
- metrics->fAscent = -size * kElegantAscent / 2048;
- metrics->fDescent = -size * kElegantDescent / 2048;
- metrics->fLeading = size * kElegantLeading / 2048;
- spacing = metrics->fDescent - metrics->fAscent + metrics->fLeading;
+
+ bool forwardTextDirection;
+ if (count < 0) {
+ forwardTextDirection = false;
+ count = -count;
}
- return spacing;
- }
-
- static jfloat ascent(JNIEnv* env, jobject, jlong paintHandle, jlong typefaceHandle) {
- Paint::FontMetrics metrics;
- getMetricsInternal(paintHandle, typefaceHandle, &metrics);
- return SkScalarToFloat(metrics.fAscent);
- }
-
- static jfloat descent(JNIEnv* env, jobject, jlong paintHandle, jlong typefaceHandle) {
- Paint::FontMetrics metrics;
- getMetricsInternal(paintHandle, typefaceHandle, &metrics);
- return SkScalarToFloat(metrics.fDescent);
- }
-
- static jfloat getFontMetrics(JNIEnv* env, jobject, jlong paintHandle,
- jlong typefaceHandle, jobject metricsObj) {
- Paint::FontMetrics metrics;
- SkScalar spacing = getMetricsInternal(paintHandle, typefaceHandle, &metrics);
-
- if (metricsObj) {
- SkASSERT(env->IsInstanceOf(metricsObj, gFontMetrics_class));
- env->SetFloatField(metricsObj, gFontMetrics_fieldID.top, SkScalarToFloat(metrics.fTop));
- env->SetFloatField(metricsObj, gFontMetrics_fieldID.ascent, SkScalarToFloat(metrics.fAscent));
- env->SetFloatField(metricsObj, gFontMetrics_fieldID.descent, SkScalarToFloat(metrics.fDescent));
- env->SetFloatField(metricsObj, gFontMetrics_fieldID.bottom, SkScalarToFloat(metrics.fBottom));
- env->SetFloatField(metricsObj, gFontMetrics_fieldID.leading, SkScalarToFloat(metrics.fLeading));
+ else {
+ forwardTextDirection = true;
}
- return SkScalarToFloat(spacing);
+
+ if ((index < 0) || (index + count > env->GetArrayLength(jtext))) {
+ doThrowAIOOBE(env);
+ return 0;
+ }
+
+ const jchar* text = env->GetCharArrayElements(jtext, nullptr);
+ count = breakText(env, *paint, typeface, text + index, count, maxWidth,
+ bidiFlags, jmeasuredWidth, forwardTextDirection);
+ env->ReleaseCharArrayElements(jtext, const_cast<jchar*>(text),
+ JNI_ABORT);
+ return count;
}
- static jint getFontMetricsInt(JNIEnv* env, jobject, jlong paintHandle,
- jlong typefaceHandle, jobject metricsObj) {
- Paint::FontMetrics metrics;
+ static jint breakTextS(JNIEnv* env, jobject clazz, jlong paintHandle, jlong typefaceHandle, jstring jtext,
+ jboolean forwards, jfloat maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) {
+ NPE_CHECK_RETURN_ZERO(env, jtext);
- getMetricsInternal(paintHandle, typefaceHandle, &metrics);
- int ascent = SkScalarRoundToInt(metrics.fAscent);
- int descent = SkScalarRoundToInt(metrics.fDescent);
- int leading = SkScalarRoundToInt(metrics.fLeading);
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
- if (metricsObj) {
- SkASSERT(env->IsInstanceOf(metricsObj, gFontMetricsInt_class));
- env->SetIntField(metricsObj, gFontMetricsInt_fieldID.top, SkScalarFloorToInt(metrics.fTop));
- env->SetIntField(metricsObj, gFontMetricsInt_fieldID.ascent, ascent);
- env->SetIntField(metricsObj, gFontMetricsInt_fieldID.descent, descent);
- env->SetIntField(metricsObj, gFontMetricsInt_fieldID.bottom, SkScalarCeilToInt(metrics.fBottom));
- env->SetIntField(metricsObj, gFontMetricsInt_fieldID.leading, leading);
- }
- return descent - ascent + leading;
+ int count = env->GetStringLength(jtext);
+ const jchar* text = env->GetStringChars(jtext, nullptr);
+ count = breakText(env, *paint, typeface, text, count, maxWidth, bidiFlags, jmeasuredWidth, forwards);
+ env->ReleaseStringChars(jtext, text);
+ return count;
}
static jfloat doTextAdvances(JNIEnv *env, Paint *paint, Typeface* typeface,
@@ -530,7 +218,7 @@
jint bidiFlags, jfloatArray advances, jint advancesIndex) {
Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
- jchar* textArray = env->GetCharArrayElements(text, NULL);
+ jchar* textArray = env->GetCharArrayElements(text, nullptr);
jfloat result = doTextAdvances(env, paint, typeface, textArray + contextIndex,
index - contextIndex, count, contextCount, bidiFlags, advances, advancesIndex);
env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
@@ -543,7 +231,7 @@
jfloatArray advances, jint advancesIndex) {
Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
- const jchar* textArray = env->GetStringChars(text, NULL);
+ const jchar* textArray = env->GetStringChars(text, nullptr);
jfloat result = doTextAdvances(env, paint, typeface, textArray + contextStart,
start - contextStart, end - start, contextEnd - contextStart, bidiFlags,
advances, advancesIndex);
@@ -562,7 +250,7 @@
static jint getTextRunCursor___C(JNIEnv* env, jobject clazz, jlong paintHandle, jcharArray text,
jint contextStart, jint contextCount, jint dir, jint offset, jint cursorOpt) {
Paint* paint = reinterpret_cast<Paint*>(paintHandle);
- jchar* textArray = env->GetCharArrayElements(text, NULL);
+ jchar* textArray = env->GetCharArrayElements(text, nullptr);
jint result = doTextRunCursor(env, paint, textArray, contextStart, contextCount, dir,
offset, cursorOpt);
env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
@@ -572,7 +260,7 @@
static jint getTextRunCursor__String(JNIEnv* env, jobject clazz, jlong paintHandle, jstring text,
jint contextStart, jint contextEnd, jint dir, jint offset, jint cursorOpt) {
Paint* paint = reinterpret_cast<Paint*>(paintHandle);
- const jchar* textArray = env->GetStringChars(text, NULL);
+ const jchar* textArray = env->GetStringChars(text, nullptr);
jint result = doTextRunCursor(env, paint, textArray, contextStart,
contextEnd - contextStart, dir, offset, cursorOpt);
env->ReleaseStringChars(text, textArray);
@@ -635,7 +323,7 @@
Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
- const jchar* textArray = env->GetCharArrayElements(text, NULL);
+ const jchar* textArray = env->GetCharArrayElements(text, nullptr);
getTextPath(env, paint, typeface, textArray + index, count, bidiFlags, x, y, path);
env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT);
}
@@ -646,103 +334,11 @@
Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
- const jchar* textArray = env->GetStringChars(text, NULL);
+ const jchar* textArray = env->GetStringChars(text, nullptr);
getTextPath(env, paint, typeface, textArray + start, end - start, bidiFlags, x, y, path);
env->ReleaseStringChars(text, textArray);
}
- static void setShadowLayer(JNIEnv* env, jobject clazz, jlong paintHandle, jfloat radius,
- jfloat dx, jfloat dy, jint color) {
- Paint* paint = reinterpret_cast<Paint*>(paintHandle);
- if (radius <= 0) {
- paint->setLooper(NULL);
- }
- else {
- SkScalar sigma = android::uirenderer::Blur::convertRadiusToSigma(radius);
- paint->setLooper(SkBlurDrawLooper::Create((SkColor)color, sigma, dx, dy))->unref();
- }
- }
-
- static jboolean hasShadowLayer(JNIEnv* env, jobject clazz, jlong paintHandle) {
- Paint* paint = reinterpret_cast<Paint*>(paintHandle);
- return paint->getLooper() && paint->getLooper()->asABlurShadow(NULL);
- }
-
- static int breakText(JNIEnv* env, const Paint& paint, Typeface* typeface, const jchar text[],
- int count, float maxWidth, jint bidiFlags, jfloatArray jmeasured,
- const bool forwardScan) {
- size_t measuredCount = 0;
- float measured = 0;
-
- std::unique_ptr<float[]> advancesArray(new float[count]);
- MinikinUtils::measureText(&paint, bidiFlags, typeface, text, 0, count, count,
- advancesArray.get());
-
- for (int i = 0; i < count; i++) {
- // traverse in the given direction
- int index = forwardScan ? i : (count - i - 1);
- float width = advancesArray[index];
- if (measured + width > maxWidth) {
- break;
- }
- // properly handle clusters when scanning backwards
- if (forwardScan || width != 0.0f) {
- measuredCount = i + 1;
- }
- measured += width;
- }
-
- if (jmeasured && env->GetArrayLength(jmeasured) > 0) {
- AutoJavaFloatArray autoMeasured(env, jmeasured, 1);
- jfloat* array = autoMeasured.ptr();
- array[0] = measured;
- }
- return measuredCount;
- }
-
- static jint breakTextC(JNIEnv* env, jobject clazz, jlong paintHandle, jlong typefaceHandle, jcharArray jtext,
- jint index, jint count, jfloat maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) {
- NPE_CHECK_RETURN_ZERO(env, jtext);
-
- Paint* paint = reinterpret_cast<Paint*>(paintHandle);
- Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
-
- bool forwardTextDirection;
- if (count < 0) {
- forwardTextDirection = false;
- count = -count;
- }
- else {
- forwardTextDirection = true;
- }
-
- if ((index < 0) || (index + count > env->GetArrayLength(jtext))) {
- doThrowAIOOBE(env);
- return 0;
- }
-
- const jchar* text = env->GetCharArrayElements(jtext, NULL);
- count = breakText(env, *paint, typeface, text + index, count, maxWidth,
- bidiFlags, jmeasuredWidth, forwardTextDirection);
- env->ReleaseCharArrayElements(jtext, const_cast<jchar*>(text),
- JNI_ABORT);
- return count;
- }
-
- static jint breakTextS(JNIEnv* env, jobject clazz, jlong paintHandle, jlong typefaceHandle, jstring jtext,
- jboolean forwards, jfloat maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) {
- NPE_CHECK_RETURN_ZERO(env, jtext);
-
- Paint* paint = reinterpret_cast<Paint*>(paintHandle);
- Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
-
- int count = env->GetStringLength(jtext);
- const jchar* text = env->GetStringChars(jtext, NULL);
- count = breakText(env, *paint, typeface, text, count, maxWidth, bidiFlags, jmeasuredWidth, forwards);
- env->ReleaseStringChars(jtext, text);
- return count;
- }
-
static void doTextBounds(JNIEnv* env, const jchar* text, int count, jobject bounds,
const Paint& paint, Typeface* typeface, jint bidiFlags) {
SkRect r;
@@ -764,7 +360,7 @@
jstring text, jint start, jint end, jint bidiFlags, jobject bounds) {
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
- const jchar* textArray = env->GetStringChars(text, NULL);
+ const jchar* textArray = env->GetStringChars(text, nullptr);
doTextBounds(env, textArray + start, end - start, bounds, *paint, typeface, bidiFlags);
env->ReleaseStringChars(text, textArray);
}
@@ -773,12 +369,28 @@
jcharArray text, jint index, jint count, jint bidiFlags, jobject bounds) {
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
- const jchar* textArray = env->GetCharArrayElements(text, NULL);
+ const jchar* textArray = env->GetCharArrayElements(text, nullptr);
doTextBounds(env, textArray + index, count, bounds, *paint, typeface, bidiFlags);
env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray),
JNI_ABORT);
}
+ // Returns true if the given string is exact one pair of regional indicators.
+ static bool isFlag(const jchar* str, size_t length) {
+ const jchar RI_LEAD_SURROGATE = 0xD83C;
+ const jchar RI_TRAIL_SURROGATE_MIN = 0xDDE6;
+ const jchar RI_TRAIL_SURROGATE_MAX = 0xDDFF;
+
+ if (length != 4) {
+ return false;
+ }
+ if (str[0] != RI_LEAD_SURROGATE || str[2] != RI_LEAD_SURROGATE) {
+ return false;
+ }
+ return RI_TRAIL_SURROGATE_MIN <= str[1] && str[1] <= RI_TRAIL_SURROGATE_MAX &&
+ RI_TRAIL_SURROGATE_MIN <= str[3] && str[3] <= RI_TRAIL_SURROGATE_MAX;
+ }
+
static jboolean layoutContainsNotdef(const minikin::Layout& layout) {
for (size_t i = 0; i < layout.nGlyphs(); i++) {
if (layout.getGlyphId(i) == 0) {
@@ -803,22 +415,6 @@
return count;
}
- // Returns true if the given string is exact one pair of regional indicators.
- static bool isFlag(const jchar* str, size_t length) {
- const jchar RI_LEAD_SURROGATE = 0xD83C;
- const jchar RI_TRAIL_SURROGATE_MIN = 0xDDE6;
- const jchar RI_TRAIL_SURROGATE_MAX = 0xDDFF;
-
- if (length != 4) {
- return false;
- }
- if (str[0] != RI_LEAD_SURROGATE || str[2] != RI_LEAD_SURROGATE) {
- return false;
- }
- return RI_TRAIL_SURROGATE_MIN <= str[1] && str[1] <= RI_TRAIL_SURROGATE_MAX &&
- RI_TRAIL_SURROGATE_MIN <= str[3] && str[3] <= RI_TRAIL_SURROGATE_MAX;
- }
-
static jboolean hasGlyph(JNIEnv *env, jclass, jlong paintHandle, jlong typefaceHandle,
jint bidiFlags, jstring string) {
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
@@ -913,7 +509,7 @@
jint contextEnd, jboolean isRtl, jint offset) {
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
- jchar* textArray = (jchar*) env->GetPrimitiveArrayCritical(text, NULL);
+ jchar* textArray = (jchar*) env->GetPrimitiveArrayCritical(text, nullptr);
jfloat result = doRunAdvance(paint, typeface, textArray + contextStart,
start - contextStart, end - start, contextEnd - contextStart, isRtl,
offset - contextStart);
@@ -935,7 +531,7 @@
jint contextEnd, jboolean isRtl, jfloat advance) {
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
- jchar* textArray = (jchar*) env->GetPrimitiveArrayCritical(text, NULL);
+ jchar* textArray = (jchar*) env->GetPrimitiveArrayCritical(text, nullptr);
jint result = doOffsetForAdvance(paint, typeface, textArray + contextStart,
start - contextStart, end - start, contextEnd - contextStart, isRtl, advance);
result += contextStart;
@@ -943,76 +539,422 @@
return result;
}
+ // ------------------ @FastNative ---------------------------
+
+ static jint setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ ScopedUtfChars localesChars(env, locales);
+ jint minikinLangListId = minikin::FontStyle::registerLanguageList(localesChars.c_str());
+ obj->setMinikinLangListId(minikinLangListId);
+ return minikinLangListId;
+ }
+
+ static void setFontFeatureSettings(JNIEnv* env, jobject clazz, jlong paintHandle, jstring settings) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ if (!settings) {
+ paint->setFontFeatureSettings(std::string());
+ } else {
+ ScopedUtfChars settingsChars(env, settings);
+ paint->setFontFeatureSettings(std::string(settingsChars.c_str(), settingsChars.size()));
+ }
+ }
+
+ static SkScalar getMetricsInternal(jlong paintHandle, jlong typefaceHandle,
+ Paint::FontMetrics *metrics) {
+ const int kElegantTop = 2500;
+ const int kElegantBottom = -1000;
+ const int kElegantAscent = 1900;
+ const int kElegantDescent = -500;
+ const int kElegantLeading = 0;
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
+ typeface = Typeface::resolveDefault(typeface);
+ minikin::FakedFont baseFont = typeface->fFontCollection->baseFontFaked(typeface->fStyle);
+ float saveSkewX = paint->getTextSkewX();
+ bool savefakeBold = paint->isFakeBoldText();
+ MinikinFontSkia::populateSkPaint(paint, baseFont.font, baseFont.fakery);
+ SkScalar spacing = paint->getFontMetrics(metrics);
+ // The populateSkPaint call may have changed fake bold / text skew
+ // because we want to measure with those effects applied, so now
+ // restore the original settings.
+ paint->setTextSkewX(saveSkewX);
+ paint->setFakeBoldText(savefakeBold);
+ if (paint->getFontVariant() == minikin::VARIANT_ELEGANT) {
+ SkScalar size = paint->getTextSize();
+ metrics->fTop = -size * kElegantTop / 2048;
+ metrics->fBottom = -size * kElegantBottom / 2048;
+ metrics->fAscent = -size * kElegantAscent / 2048;
+ metrics->fDescent = -size * kElegantDescent / 2048;
+ metrics->fLeading = size * kElegantLeading / 2048;
+ spacing = metrics->fDescent - metrics->fAscent + metrics->fLeading;
+ }
+ return spacing;
+ }
+
+ static jfloat getFontMetrics(JNIEnv* env, jobject, jlong paintHandle,
+ jlong typefaceHandle, jobject metricsObj) {
+ Paint::FontMetrics metrics;
+ SkScalar spacing = getMetricsInternal(paintHandle, typefaceHandle, &metrics);
+
+ if (metricsObj) {
+ SkASSERT(env->IsInstanceOf(metricsObj, gFontMetrics_class));
+ env->SetFloatField(metricsObj, gFontMetrics_fieldID.top, SkScalarToFloat(metrics.fTop));
+ env->SetFloatField(metricsObj, gFontMetrics_fieldID.ascent, SkScalarToFloat(metrics.fAscent));
+ env->SetFloatField(metricsObj, gFontMetrics_fieldID.descent, SkScalarToFloat(metrics.fDescent));
+ env->SetFloatField(metricsObj, gFontMetrics_fieldID.bottom, SkScalarToFloat(metrics.fBottom));
+ env->SetFloatField(metricsObj, gFontMetrics_fieldID.leading, SkScalarToFloat(metrics.fLeading));
+ }
+ return SkScalarToFloat(spacing);
+ }
+
+ static jint getFontMetricsInt(JNIEnv* env, jobject, jlong paintHandle,
+ jlong typefaceHandle, jobject metricsObj) {
+ Paint::FontMetrics metrics;
+
+ getMetricsInternal(paintHandle, typefaceHandle, &metrics);
+ int ascent = SkScalarRoundToInt(metrics.fAscent);
+ int descent = SkScalarRoundToInt(metrics.fDescent);
+ int leading = SkScalarRoundToInt(metrics.fLeading);
+
+ if (metricsObj) {
+ SkASSERT(env->IsInstanceOf(metricsObj, gFontMetricsInt_class));
+ env->SetIntField(metricsObj, gFontMetricsInt_fieldID.top, SkScalarFloorToInt(metrics.fTop));
+ env->SetIntField(metricsObj, gFontMetricsInt_fieldID.ascent, ascent);
+ env->SetIntField(metricsObj, gFontMetricsInt_fieldID.descent, descent);
+ env->SetIntField(metricsObj, gFontMetricsInt_fieldID.bottom, SkScalarCeilToInt(metrics.fBottom));
+ env->SetIntField(metricsObj, gFontMetricsInt_fieldID.leading, leading);
+ }
+ return descent - ascent + leading;
+ }
+
+
+ // ------------------ @CriticalNative ---------------------------
+
+ static void reset(jlong objHandle) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ obj->reset();
+ defaultSettingsForAndroid(obj);
+ }
+
+ static void assign(jlong dstPaintHandle, jlong srcPaintHandle) {
+ Paint* dst = reinterpret_cast<Paint*>(dstPaintHandle);
+ const Paint* src = reinterpret_cast<Paint*>(srcPaintHandle);
+ *dst = *src;
+ }
+
+ // Equivalent to the Java Paint's FILTER_BITMAP_FLAG.
+ static const uint32_t sFilterBitmapFlag = 0x02;
+
+ static jint getFlags(jlong paintHandle) {
+ Paint* nativePaint = reinterpret_cast<Paint*>(paintHandle);
+ uint32_t result = nativePaint->getFlags();
+ result &= ~sFilterBitmapFlag; // Filtering no longer stored in this bit. Mask away.
+ if (nativePaint->getFilterQuality() != kNone_SkFilterQuality) {
+ result |= sFilterBitmapFlag;
+ }
+ return static_cast<jint>(result);
+ }
+
+ static void setFlags(jlong paintHandle, jint flags) {
+ Paint* nativePaint = reinterpret_cast<Paint*>(paintHandle);
+ // Instead of modifying 0x02, change the filter level.
+ nativePaint->setFilterQuality(flags & sFilterBitmapFlag
+ ? kLow_SkFilterQuality
+ : kNone_SkFilterQuality);
+ // Don't pass through filter flag, which is no longer stored in paint's flags.
+ flags &= ~sFilterBitmapFlag;
+ // Use the existing value for 0x02.
+ const uint32_t existing0x02Flag = nativePaint->getFlags() & sFilterBitmapFlag;
+ flags |= existing0x02Flag;
+ nativePaint->setFlags(flags);
+ }
+
+ static jint getHinting(jlong paintHandle) {
+ return reinterpret_cast<Paint*>(paintHandle)->getHinting()
+ == Paint::kNo_Hinting ? 0 : 1;
+ }
+
+ static void setHinting(jlong paintHandle, jint mode) {
+ reinterpret_cast<Paint*>(paintHandle)->setHinting(
+ mode == 0 ? Paint::kNo_Hinting : Paint::kNormal_Hinting);
+ }
+
+ static void setAntiAlias(jlong paintHandle, jboolean aa) {
+ reinterpret_cast<Paint*>(paintHandle)->setAntiAlias(aa);
+ }
+
+ static void setLinearText(jlong paintHandle, jboolean linearText) {
+ reinterpret_cast<Paint*>(paintHandle)->setLinearText(linearText);
+ }
+
+ static void setSubpixelText(jlong paintHandle, jboolean subpixelText) {
+ reinterpret_cast<Paint*>(paintHandle)->setSubpixelText(subpixelText);
+ }
+
+ static void setUnderlineText(jlong paintHandle, jboolean underlineText) {
+ reinterpret_cast<Paint*>(paintHandle)->setUnderlineText(underlineText);
+ }
+
+ static void setStrikeThruText(jlong paintHandle, jboolean strikeThruText) {
+ reinterpret_cast<Paint*>(paintHandle)->setStrikeThruText(strikeThruText);
+ }
+
+ static void setFakeBoldText(jlong paintHandle, jboolean fakeBoldText) {
+ reinterpret_cast<Paint*>(paintHandle)->setFakeBoldText(fakeBoldText);
+ }
+
+ static void setFilterBitmap(jlong paintHandle, jboolean filterBitmap) {
+ reinterpret_cast<Paint*>(paintHandle)->setFilterQuality(
+ filterBitmap ? kLow_SkFilterQuality : kNone_SkFilterQuality);
+ }
+
+ static void setDither(jlong paintHandle, jboolean dither) {
+ reinterpret_cast<Paint*>(paintHandle)->setDither(dither);
+ }
+
+ static jint getStyle(jlong objHandle) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ return static_cast<jint>(obj->getStyle());
+ }
+
+ static void setStyle(jlong objHandle, jint styleHandle) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ Paint::Style style = static_cast<Paint::Style>(styleHandle);
+ obj->setStyle(style);
+ }
+
+ static jint getColor(jlong paintHandle) {
+ int color;
+ color = reinterpret_cast<Paint*>(paintHandle)->getColor();
+ return static_cast<jint>(color);
+ }
+
+ static jint getAlpha(jlong paintHandle) {
+ int alpha;
+ alpha = reinterpret_cast<Paint*>(paintHandle)->getAlpha();
+ return static_cast<jint>(alpha);
+ }
+
+ static void setColor(jlong paintHandle, jint color) {
+ reinterpret_cast<Paint*>(paintHandle)->setColor(color);
+ }
+
+ static void setAlpha(jlong paintHandle, jint a) {
+ reinterpret_cast<Paint*>(paintHandle)->setAlpha(a);
+ }
+
+ static jfloat getStrokeWidth(jlong paintHandle) {
+ return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getStrokeWidth());
+ }
+
+ static void setStrokeWidth(jlong paintHandle, jfloat width) {
+ reinterpret_cast<Paint*>(paintHandle)->setStrokeWidth(width);
+ }
+
+ static jfloat getStrokeMiter(jlong paintHandle) {
+ return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getStrokeMiter());
+ }
+
+ static void setStrokeMiter(jlong paintHandle, jfloat miter) {
+ reinterpret_cast<Paint*>(paintHandle)->setStrokeMiter(miter);
+ }
+
+ static jint getStrokeCap(jlong objHandle) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ return static_cast<jint>(obj->getStrokeCap());
+ }
+
+ static void setStrokeCap(jlong objHandle, jint capHandle) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ Paint::Cap cap = static_cast<Paint::Cap>(capHandle);
+ obj->setStrokeCap(cap);
+ }
+
+ static jint getStrokeJoin(jlong objHandle) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ return static_cast<jint>(obj->getStrokeJoin());
+ }
+
+ static void setStrokeJoin(jlong objHandle, jint joinHandle) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ Paint::Join join = (Paint::Join) joinHandle;
+ obj->setStrokeJoin(join);
+ }
+
+ static jboolean getFillPath(jlong objHandle, jlong srcHandle, jlong dstHandle) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ SkPath* src = reinterpret_cast<SkPath*>(srcHandle);
+ SkPath* dst = reinterpret_cast<SkPath*>(dstHandle);
+ return obj->getFillPath(*src, dst) ? JNI_TRUE : JNI_FALSE;
+ }
+
+ static jlong setShader(jlong objHandle, jlong shaderHandle) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
+ return reinterpret_cast<jlong>(obj->setShader(shader));
+ }
+
+ static jlong setColorFilter(jlong objHandle, jlong filterHandle) {
+ Paint* obj = reinterpret_cast<Paint *>(objHandle);
+ SkColorFilter* filter = reinterpret_cast<SkColorFilter *>(filterHandle);
+ return reinterpret_cast<jlong>(obj->setColorFilter(filter));
+ }
+
+ static void setXfermode(jlong paintHandle, jint xfermodeHandle) {
+ // validate that the Java enum values match our expectations
+ static_assert(0 == SkXfermode::kClear_Mode, "xfermode_mismatch");
+ static_assert(1 == SkXfermode::kSrc_Mode, "xfermode_mismatch");
+ static_assert(2 == SkXfermode::kDst_Mode, "xfermode_mismatch");
+ static_assert(3 == SkXfermode::kSrcOver_Mode, "xfermode_mismatch");
+ static_assert(4 == SkXfermode::kDstOver_Mode, "xfermode_mismatch");
+ static_assert(5 == SkXfermode::kSrcIn_Mode, "xfermode_mismatch");
+ static_assert(6 == SkXfermode::kDstIn_Mode, "xfermode_mismatch");
+ static_assert(7 == SkXfermode::kSrcOut_Mode, "xfermode_mismatch");
+ static_assert(8 == SkXfermode::kDstOut_Mode, "xfermode_mismatch");
+ static_assert(9 == SkXfermode::kSrcATop_Mode, "xfermode_mismatch");
+ static_assert(10 == SkXfermode::kDstATop_Mode, "xfermode_mismatch");
+ static_assert(11 == SkXfermode::kXor_Mode, "xfermode_mismatch");
+ static_assert(16 == SkXfermode::kDarken_Mode, "xfermode_mismatch");
+ static_assert(17 == SkXfermode::kLighten_Mode, "xfermode_mismatch");
+ static_assert(13 == SkXfermode::kModulate_Mode, "xfermode_mismatch");
+ static_assert(14 == SkXfermode::kScreen_Mode, "xfermode_mismatch");
+ static_assert(12 == SkXfermode::kPlus_Mode, "xfermode_mismatch");
+ static_assert(15 == SkXfermode::kOverlay_Mode, "xfermode_mismatch");
+
+ SkXfermode::Mode mode = static_cast<SkXfermode::Mode>(xfermodeHandle);
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ paint->setXfermodeMode(mode);
+ }
+
+ static jlong setPathEffect(jlong objHandle, jlong effectHandle) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ SkPathEffect* effect = reinterpret_cast<SkPathEffect*>(effectHandle);
+ return reinterpret_cast<jlong>(obj->setPathEffect(effect));
+ }
+
+ static jlong setMaskFilter(jlong objHandle, jlong maskfilterHandle) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ SkMaskFilter* maskfilter = reinterpret_cast<SkMaskFilter*>(maskfilterHandle);
+ return reinterpret_cast<jlong>(obj->setMaskFilter(maskfilter));
+ }
+
+ static jlong setTypeface(jlong objHandle, jlong typefaceHandle) {
+ // TODO: in Paint refactoring, set typeface on android Paint, not Paint
+ return 0;
+ }
+
+ static jlong setRasterizer(jlong objHandle, jlong rasterizerHandle) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ SkAutoTUnref<SkRasterizer> rasterizer(GraphicsJNI::refNativeRasterizer(rasterizerHandle));
+ return reinterpret_cast<jlong>(obj->setRasterizer(rasterizer));
+ }
+
+ static jint getTextAlign(jlong objHandle) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ return static_cast<jint>(obj->getTextAlign());
+ }
+
+ static void setTextAlign(jlong objHandle, jint alignHandle) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ Paint::Align align = static_cast<Paint::Align>(alignHandle);
+ obj->setTextAlign(align);
+ }
+
+ static void setTextLocalesByMinikinLangListId(jlong objHandle,
+ jint minikinLangListId) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ obj->setMinikinLangListId(minikinLangListId);
+ }
+
+ static jboolean isElegantTextHeight(jlong paintHandle) {
+ Paint* obj = reinterpret_cast<Paint*>(paintHandle);
+ return obj->getFontVariant() == minikin::VARIANT_ELEGANT;
+ }
+
+ static void setElegantTextHeight(jlong paintHandle, jboolean aa) {
+ Paint* obj = reinterpret_cast<Paint*>(paintHandle);
+ obj->setFontVariant(aa ? minikin::VARIANT_ELEGANT : minikin::VARIANT_DEFAULT);
+ }
+
+ static jfloat getTextSize(jlong paintHandle) {
+ return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getTextSize());
+ }
+
+ static void setTextSize(jlong paintHandle, jfloat textSize) {
+ reinterpret_cast<Paint*>(paintHandle)->setTextSize(textSize);
+ }
+
+ static jfloat getTextScaleX(jlong paintHandle) {
+ return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getTextScaleX());
+ }
+
+ static void setTextScaleX(jlong paintHandle, jfloat scaleX) {
+ reinterpret_cast<Paint*>(paintHandle)->setTextScaleX(scaleX);
+ }
+
+ static jfloat getTextSkewX(jlong paintHandle) {
+ return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getTextSkewX());
+ }
+
+ static void setTextSkewX(jlong paintHandle, jfloat skewX) {
+ reinterpret_cast<Paint*>(paintHandle)->setTextSkewX(skewX);
+ }
+
+ static jfloat getLetterSpacing(jlong paintHandle) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ return paint->getLetterSpacing();
+ }
+
+ static void setLetterSpacing(jlong paintHandle, jfloat letterSpacing) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ paint->setLetterSpacing(letterSpacing);
+ }
+
+ static jint getHyphenEdit(jlong paintHandle, jint hyphen) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ return paint->getHyphenEdit();
+ }
+
+ static void setHyphenEdit(jlong paintHandle, jint hyphen) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ paint->setHyphenEdit((uint32_t)hyphen);
+ }
+
+ static jfloat ascent(jlong paintHandle, jlong typefaceHandle) {
+ Paint::FontMetrics metrics;
+ getMetricsInternal(paintHandle, typefaceHandle, &metrics);
+ return SkScalarToFloat(metrics.fAscent);
+ }
+
+ static jfloat descent(jlong paintHandle, jlong typefaceHandle) {
+ Paint::FontMetrics metrics;
+ getMetricsInternal(paintHandle, typefaceHandle, &metrics);
+ return SkScalarToFloat(metrics.fDescent);
+ }
+
+ static void setShadowLayer(jlong paintHandle, jfloat radius,
+ jfloat dx, jfloat dy, jint color) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ if (radius <= 0) {
+ paint->setLooper(nullptr);
+ }
+ else {
+ SkScalar sigma = android::uirenderer::Blur::convertRadiusToSigma(radius);
+ paint->setLooper(SkBlurDrawLooper::Create((SkColor)color, sigma, dx, dy))->unref();
+ }
+ }
+
+ static jboolean hasShadowLayer(jlong paintHandle) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ return paint->getLooper() && paint->getLooper()->asABlurShadow(nullptr);
+ }
+
}; // namespace PaintGlue
static const JNINativeMethod methods[] = {
{"nGetNativeFinalizer", "()J", (void*) PaintGlue::getNativeFinalizer},
{"nInit","()J", (void*) PaintGlue::init},
{"nInitWithPaint","(J)J", (void*) PaintGlue::initWithPaint},
-
- {"nReset","!(J)V", (void*) PaintGlue::reset},
- {"nSet","!(JJ)V", (void*) PaintGlue::assign},
- {"nGetFlags","!(J)I", (void*) PaintGlue::getFlags},
- {"nSetFlags","!(JI)V", (void*) PaintGlue::setFlags},
- {"nGetHinting","!(J)I", (void*) PaintGlue::getHinting},
- {"nSetHinting","!(JI)V", (void*) PaintGlue::setHinting},
- {"nSetAntiAlias","!(JZ)V", (void*) PaintGlue::setAntiAlias},
- {"nSetSubpixelText","!(JZ)V", (void*) PaintGlue::setSubpixelText},
- {"nSetLinearText","!(JZ)V", (void*) PaintGlue::setLinearText},
- {"nSetUnderlineText","!(JZ)V", (void*) PaintGlue::setUnderlineText},
- {"nSetStrikeThruText","!(JZ)V", (void*) PaintGlue::setStrikeThruText},
- {"nSetFakeBoldText","!(JZ)V", (void*) PaintGlue::setFakeBoldText},
- {"nSetFilterBitmap","!(JZ)V", (void*) PaintGlue::setFilterBitmap},
- {"nSetDither","!(JZ)V", (void*) PaintGlue::setDither},
- {"nGetStyle","!(J)I", (void*) PaintGlue::getStyle},
- {"nSetStyle","!(JI)V", (void*) PaintGlue::setStyle},
- {"nGetColor","!(J)I", (void*) PaintGlue::getColor},
- {"nSetColor","!(JI)V", (void*) PaintGlue::setColor},
- {"nGetAlpha","!(J)I", (void*) PaintGlue::getAlpha},
- {"nSetAlpha","!(JI)V", (void*) PaintGlue::setAlpha},
- {"nGetStrokeWidth","!(J)F", (void*) PaintGlue::getStrokeWidth},
- {"nSetStrokeWidth","!(JF)V", (void*) PaintGlue::setStrokeWidth},
- {"nGetStrokeMiter","!(J)F", (void*) PaintGlue::getStrokeMiter},
- {"nSetStrokeMiter","!(JF)V", (void*) PaintGlue::setStrokeMiter},
- {"nGetStrokeCap","!(J)I", (void*) PaintGlue::getStrokeCap},
- {"nSetStrokeCap","!(JI)V", (void*) PaintGlue::setStrokeCap},
- {"nGetStrokeJoin","!(J)I", (void*) PaintGlue::getStrokeJoin},
- {"nSetStrokeJoin","!(JI)V", (void*) PaintGlue::setStrokeJoin},
- {"nGetFillPath","!(JJJ)Z", (void*) PaintGlue::getFillPath},
- {"nSetShader","!(JJ)J", (void*) PaintGlue::setShader},
- {"nSetColorFilter","!(JJ)J", (void*) PaintGlue::setColorFilter},
- {"nSetXfermode","!(JI)V", (void*) PaintGlue::setXfermode},
- {"nSetPathEffect","!(JJ)J", (void*) PaintGlue::setPathEffect},
- {"nSetMaskFilter","!(JJ)J", (void*) PaintGlue::setMaskFilter},
- {"nSetTypeface","!(JJ)J", (void*) PaintGlue::setTypeface},
- {"nSetRasterizer","!(JJ)J", (void*) PaintGlue::setRasterizer},
- {"nGetTextAlign","!(J)I", (void*) PaintGlue::getTextAlign},
- {"nSetTextAlign","!(JI)V", (void*) PaintGlue::setTextAlign},
- {"nSetTextLocales","!(JLjava/lang/String;)I", (void*) PaintGlue::setTextLocales},
- {"nSetTextLocalesByMinikinLangListId","!(JI)V",
- (void*) PaintGlue::setTextLocalesByMinikinLangListId},
- {"nIsElegantTextHeight","!(J)Z", (void*) PaintGlue::isElegantTextHeight},
- {"nSetElegantTextHeight","!(JZ)V", (void*) PaintGlue::setElegantTextHeight},
- {"nGetTextSize","!(J)F", (void*) PaintGlue::getTextSize},
- {"nSetTextSize","!(JF)V", (void*) PaintGlue::setTextSize},
- {"nGetTextScaleX","!(J)F", (void*) PaintGlue::getTextScaleX},
- {"nSetTextScaleX","!(JF)V", (void*) PaintGlue::setTextScaleX},
- {"nGetTextSkewX","!(J)F", (void*) PaintGlue::getTextSkewX},
- {"nSetTextSkewX","!(JF)V", (void*) PaintGlue::setTextSkewX},
- {"nGetLetterSpacing","!(J)F", (void*) PaintGlue::getLetterSpacing},
- {"nSetLetterSpacing","!(JF)V", (void*) PaintGlue::setLetterSpacing},
- {"nSetFontFeatureSettings","(JLjava/lang/String;)V",
- (void*) PaintGlue::setFontFeatureSettings},
- {"nGetHyphenEdit", "!(J)I", (void*) PaintGlue::getHyphenEdit},
- {"nSetHyphenEdit", "!(JI)V", (void*) PaintGlue::setHyphenEdit},
- {"nAscent","!(JJ)F", (void*) PaintGlue::ascent},
- {"nDescent","!(JJ)F", (void*) PaintGlue::descent},
-
- {"nGetFontMetrics", "!(JJLandroid/graphics/Paint$FontMetrics;)F",
- (void*)PaintGlue::getFontMetrics},
- {"nGetFontMetricsInt", "!(JJLandroid/graphics/Paint$FontMetricsInt;)I",
- (void*)PaintGlue::getFontMetricsInt},
-
{"nBreakText","(JJ[CIIFI[F)I", (void*) PaintGlue::breakTextC},
{"nBreakText","(JJLjava/lang/String;ZFI[F)I", (void*) PaintGlue::breakTextS},
{"nGetTextAdvances","(JJ[CIIIII[FI)F",
@@ -1034,8 +976,74 @@
{"nGetOffsetForAdvance", "(JJ[CIIIIZF)I",
(void*) PaintGlue::getOffsetForAdvance___CIIIIZF_I},
- {"nSetShadowLayer", "!(JFFFI)V", (void*)PaintGlue::setShadowLayer},
- {"nHasShadowLayer", "!(J)Z", (void*)PaintGlue::hasShadowLayer}
+ // --------------- @FastNative ----------------------
+
+ {"nSetTextLocales","(JLjava/lang/String;)I", (void*) PaintGlue::setTextLocales},
+ {"nSetFontFeatureSettings","(JLjava/lang/String;)V",
+ (void*) PaintGlue::setFontFeatureSettings},
+ {"nGetFontMetrics", "(JJLandroid/graphics/Paint$FontMetrics;)F",
+ (void*)PaintGlue::getFontMetrics},
+ {"nGetFontMetricsInt", "(JJLandroid/graphics/Paint$FontMetricsInt;)I",
+ (void*)PaintGlue::getFontMetricsInt},
+
+ // --------------- @CriticalNative ------------------
+
+ {"nReset","(J)V", (void*) PaintGlue::reset},
+ {"nSet","(JJ)V", (void*) PaintGlue::assign},
+ {"nGetFlags","(J)I", (void*) PaintGlue::getFlags},
+ {"nSetFlags","(JI)V", (void*) PaintGlue::setFlags},
+ {"nGetHinting","(J)I", (void*) PaintGlue::getHinting},
+ {"nSetHinting","(JI)V", (void*) PaintGlue::setHinting},
+ {"nSetAntiAlias","(JZ)V", (void*) PaintGlue::setAntiAlias},
+ {"nSetSubpixelText","(JZ)V", (void*) PaintGlue::setSubpixelText},
+ {"nSetLinearText","(JZ)V", (void*) PaintGlue::setLinearText},
+ {"nSetUnderlineText","(JZ)V", (void*) PaintGlue::setUnderlineText},
+ {"nSetStrikeThruText","(JZ)V", (void*) PaintGlue::setStrikeThruText},
+ {"nSetFakeBoldText","(JZ)V", (void*) PaintGlue::setFakeBoldText},
+ {"nSetFilterBitmap","(JZ)V", (void*) PaintGlue::setFilterBitmap},
+ {"nSetDither","(JZ)V", (void*) PaintGlue::setDither},
+ {"nGetStyle","(J)I", (void*) PaintGlue::getStyle},
+ {"nSetStyle","(JI)V", (void*) PaintGlue::setStyle},
+ {"nGetColor","(J)I", (void*) PaintGlue::getColor},
+ {"nSetColor","(JI)V", (void*) PaintGlue::setColor},
+ {"nGetAlpha","(J)I", (void*) PaintGlue::getAlpha},
+ {"nSetAlpha","(JI)V", (void*) PaintGlue::setAlpha},
+ {"nGetStrokeWidth","(J)F", (void*) PaintGlue::getStrokeWidth},
+ {"nSetStrokeWidth","(JF)V", (void*) PaintGlue::setStrokeWidth},
+ {"nGetStrokeMiter","(J)F", (void*) PaintGlue::getStrokeMiter},
+ {"nSetStrokeMiter","(JF)V", (void*) PaintGlue::setStrokeMiter},
+ {"nGetStrokeCap","(J)I", (void*) PaintGlue::getStrokeCap},
+ {"nSetStrokeCap","(JI)V", (void*) PaintGlue::setStrokeCap},
+ {"nGetStrokeJoin","(J)I", (void*) PaintGlue::getStrokeJoin},
+ {"nSetStrokeJoin","(JI)V", (void*) PaintGlue::setStrokeJoin},
+ {"nGetFillPath","(JJJ)Z", (void*) PaintGlue::getFillPath},
+ {"nSetShader","(JJ)J", (void*) PaintGlue::setShader},
+ {"nSetColorFilter","(JJ)J", (void*) PaintGlue::setColorFilter},
+ {"nSetXfermode","(JI)V", (void*) PaintGlue::setXfermode},
+ {"nSetPathEffect","(JJ)J", (void*) PaintGlue::setPathEffect},
+ {"nSetMaskFilter","(JJ)J", (void*) PaintGlue::setMaskFilter},
+ {"nSetTypeface","(JJ)J", (void*) PaintGlue::setTypeface},
+ {"nSetRasterizer","(JJ)J", (void*) PaintGlue::setRasterizer},
+ {"nGetTextAlign","(J)I", (void*) PaintGlue::getTextAlign},
+ {"nSetTextAlign","(JI)V", (void*) PaintGlue::setTextAlign},
+ {"nSetTextLocalesByMinikinLangListId","(JI)V",
+ (void*) PaintGlue::setTextLocalesByMinikinLangListId},
+ {"nIsElegantTextHeight","(J)Z", (void*) PaintGlue::isElegantTextHeight},
+ {"nSetElegantTextHeight","(JZ)V", (void*) PaintGlue::setElegantTextHeight},
+ {"nGetTextSize","(J)F", (void*) PaintGlue::getTextSize},
+ {"nSetTextSize","(JF)V", (void*) PaintGlue::setTextSize},
+ {"nGetTextScaleX","(J)F", (void*) PaintGlue::getTextScaleX},
+ {"nSetTextScaleX","(JF)V", (void*) PaintGlue::setTextScaleX},
+ {"nGetTextSkewX","(J)F", (void*) PaintGlue::getTextSkewX},
+ {"nSetTextSkewX","(JF)V", (void*) PaintGlue::setTextSkewX},
+ {"nGetLetterSpacing","(J)F", (void*) PaintGlue::getLetterSpacing},
+ {"nSetLetterSpacing","(JF)V", (void*) PaintGlue::setLetterSpacing},
+ {"nGetHyphenEdit", "(J)I", (void*) PaintGlue::getHyphenEdit},
+ {"nSetHyphenEdit", "(JI)V", (void*) PaintGlue::setHyphenEdit},
+ {"nAscent","(JJ)F", (void*) PaintGlue::ascent},
+ {"nDescent","(JJ)F", (void*) PaintGlue::descent},
+ {"nSetShadowLayer", "(JFFFI)V", (void*)PaintGlue::setShadowLayer},
+ {"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer}
};
int register_android_graphics_Paint(JNIEnv* env) {
diff --git a/core/jni/android_hardware_location_ContextHubService.cpp b/core/jni/android_hardware_location_ContextHubService.cpp
index 8eb39e1..fbccfd5 100644
--- a/core/jni/android_hardware_location_ContextHubService.cpp
+++ b/core/jni/android_hardware_location_ContextHubService.cpp
@@ -33,6 +33,7 @@
#include <unordered_map>
#include <queue>
+#include <android-base/macros.h>
#include <cutils/log.h>
#include "JNIHelp.h"
@@ -704,10 +705,10 @@
}
jbyteArray jmsg = env->NewByteArray(msgLen);
- jintArray jheader = env->NewIntArray(sizeof(header));
+ jintArray jheader = env->NewIntArray(arraysize(header));
env->SetByteArrayRegion(jmsg, 0, msgLen, (jbyte *)msg);
- env->SetIntArrayRegion(jheader, 0, sizeof(header), (jint *)header);
+ env->SetIntArrayRegion(jheader, 0, arraysize(header), (jint *)header);
ALOGI("Passing msg type %" PRIu32 " from app %" PRIu32 " from hub %" PRIu32,
header[HEADER_FIELD_MSG_TYPE], header[HEADER_FIELD_APP_INSTANCE],
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 97c7f04..ba9facf 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -430,6 +430,8 @@
char tmp[128];
FILE *fp;
+ *foundSwapPss = false;
+
sprintf(tmp, "/proc/%d/smaps", pid);
fp = fopen(tmp, "r");
if (fp == 0) return;
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index 7da0314..13e3f0d 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -266,6 +266,9 @@
return NULL;
}
+ LOG(INFO) << "Starting thread pool.";
+ ::android::hardware::ProcessState::self()->startThreadPool();
+
return JHwRemoteBinder::NewObject(env, service);
}
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index 0a8ae2b..8f7908a 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -722,33 +722,50 @@
// ----------------------------------------------------------------------------
static const JNINativeMethod gParcelMethods[] = {
- {"nativeDataSize", "!(J)I", (void*)android_os_Parcel_dataSize},
- {"nativeDataAvail", "!(J)I", (void*)android_os_Parcel_dataAvail},
- {"nativeDataPosition", "!(J)I", (void*)android_os_Parcel_dataPosition},
- {"nativeDataCapacity", "!(J)I", (void*)android_os_Parcel_dataCapacity},
- {"nativeSetDataSize", "!(JI)J", (void*)android_os_Parcel_setDataSize},
- {"nativeSetDataPosition", "!(JI)V", (void*)android_os_Parcel_setDataPosition},
- {"nativeSetDataCapacity", "!(JI)V", (void*)android_os_Parcel_setDataCapacity},
+ // @FastNative
+ {"nativeDataSize", "(J)I", (void*)android_os_Parcel_dataSize},
+ // @FastNative
+ {"nativeDataAvail", "(J)I", (void*)android_os_Parcel_dataAvail},
+ // @FastNative
+ {"nativeDataPosition", "(J)I", (void*)android_os_Parcel_dataPosition},
+ // @FastNative
+ {"nativeDataCapacity", "(J)I", (void*)android_os_Parcel_dataCapacity},
+ // @FastNative
+ {"nativeSetDataSize", "(JI)J", (void*)android_os_Parcel_setDataSize},
+ // @FastNative
+ {"nativeSetDataPosition", "(JI)V", (void*)android_os_Parcel_setDataPosition},
+ // @FastNative
+ {"nativeSetDataCapacity", "(JI)V", (void*)android_os_Parcel_setDataCapacity},
- {"nativePushAllowFds", "!(JZ)Z", (void*)android_os_Parcel_pushAllowFds},
- {"nativeRestoreAllowFds", "!(JZ)V", (void*)android_os_Parcel_restoreAllowFds},
+ // @FastNative
+ {"nativePushAllowFds", "(JZ)Z", (void*)android_os_Parcel_pushAllowFds},
+ // @FastNative
+ {"nativeRestoreAllowFds", "(JZ)V", (void*)android_os_Parcel_restoreAllowFds},
{"nativeWriteByteArray", "(J[BII)V", (void*)android_os_Parcel_writeNative},
{"nativeWriteBlob", "(J[BII)V", (void*)android_os_Parcel_writeBlob},
- {"nativeWriteInt", "!(JI)V", (void*)android_os_Parcel_writeInt},
- {"nativeWriteLong", "!(JJ)V", (void*)android_os_Parcel_writeLong},
- {"nativeWriteFloat", "!(JF)V", (void*)android_os_Parcel_writeFloat},
- {"nativeWriteDouble", "!(JD)V", (void*)android_os_Parcel_writeDouble},
+ // @FastNative
+ {"nativeWriteInt", "(JI)V", (void*)android_os_Parcel_writeInt},
+ // @FastNative
+ {"nativeWriteLong", "(JJ)V", (void*)android_os_Parcel_writeLong},
+ // @FastNative
+ {"nativeWriteFloat", "(JF)V", (void*)android_os_Parcel_writeFloat},
+ // @FastNative
+ {"nativeWriteDouble", "(JD)V", (void*)android_os_Parcel_writeDouble},
{"nativeWriteString", "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeString},
{"nativeWriteStrongBinder", "(JLandroid/os/IBinder;)V", (void*)android_os_Parcel_writeStrongBinder},
{"nativeWriteFileDescriptor", "(JLjava/io/FileDescriptor;)J", (void*)android_os_Parcel_writeFileDescriptor},
{"nativeCreateByteArray", "(J)[B", (void*)android_os_Parcel_createByteArray},
{"nativeReadBlob", "(J)[B", (void*)android_os_Parcel_readBlob},
- {"nativeReadInt", "!(J)I", (void*)android_os_Parcel_readInt},
- {"nativeReadLong", "!(J)J", (void*)android_os_Parcel_readLong},
- {"nativeReadFloat", "!(J)F", (void*)android_os_Parcel_readFloat},
- {"nativeReadDouble", "!(J)D", (void*)android_os_Parcel_readDouble},
+ // @FastNative
+ {"nativeReadInt", "(J)I", (void*)android_os_Parcel_readInt},
+ // @FastNative
+ {"nativeReadLong", "(J)J", (void*)android_os_Parcel_readLong},
+ // @FastNative
+ {"nativeReadFloat", "(J)F", (void*)android_os_Parcel_readFloat},
+ // @FastNative
+ {"nativeReadDouble", "(J)D", (void*)android_os_Parcel_readDouble},
{"nativeReadString", "(J)Ljava/lang/String;", (void*)android_os_Parcel_readString},
{"nativeReadStrongBinder", "(J)Landroid/os/IBinder;", (void*)android_os_Parcel_readStrongBinder},
{"nativeReadFileDescriptor", "(J)Ljava/io/FileDescriptor;", (void*)android_os_Parcel_readFileDescriptor},
@@ -765,7 +782,8 @@
{"nativeMarshall", "(J)[B", (void*)android_os_Parcel_marshall},
{"nativeUnmarshall", "(J[BII)J", (void*)android_os_Parcel_unmarshall},
{"nativeAppendFrom", "(JJII)J", (void*)android_os_Parcel_appendFrom},
- {"nativeHasFileDescriptors", "!(J)Z", (void*)android_os_Parcel_hasFileDescriptors},
+ // @FastNative
+ {"nativeHasFileDescriptors", "(J)Z", (void*)android_os_Parcel_hasFileDescriptors},
{"nativeWriteInterfaceToken", "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeInterfaceToken},
{"nativeEnforceInterface", "(JLjava/lang/String;)V", (void*)android_os_Parcel_enforceInterface},
diff --git a/core/jni/android_os_SystemClock.cpp b/core/jni/android_os_SystemClock.cpp
index ccb833a..2fade69 100644
--- a/core/jni/android_os_SystemClock.cpp
+++ b/core/jni/android_os_SystemClock.cpp
@@ -36,29 +36,18 @@
namespace android {
-/*
- * native public static long uptimeMillis();
- */
-static jlong android_os_SystemClock_uptimeMillis(JNIEnv* env,
- jobject clazz)
-{
- return (jlong)uptimeMillis();
-}
-
-/*
- * native public static long elapsedRealtime();
- */
-static jlong android_os_SystemClock_elapsedRealtime(JNIEnv* env,
- jobject clazz)
-{
- return (jlong)elapsedRealtime();
-}
+static_assert(std::is_same<int64_t, jlong>::value, "jlong isn't an int64_t");
+static_assert(std::is_same<decltype(uptimeMillis()), int64_t>::value,
+ "uptimeMillis signature change, expected int64_t return value");
+static_assert(std::is_same<decltype(elapsedRealtime()), int64_t>::value,
+ "uptimeMillis signature change, expected int64_t return value");
+static_assert(std::is_same<decltype(elapsedRealtimeNano()), int64_t>::value,
+ "uptimeMillis signature change, expected int64_t return value");
/*
* native public static long currentThreadTimeMillis();
*/
-static jlong android_os_SystemClock_currentThreadTimeMillis(JNIEnv* env,
- jobject clazz)
+static jlong android_os_SystemClock_currentThreadTimeMillis()
{
struct timespec tm;
@@ -70,8 +59,7 @@
/*
* native public static long currentThreadTimeMicro();
*/
-static jlong android_os_SystemClock_currentThreadTimeMicro(JNIEnv* env,
- jobject clazz)
+static jlong android_os_SystemClock_currentThreadTimeMicro()
{
struct timespec tm;
@@ -83,8 +71,7 @@
/*
* native public static long currentTimeMicro();
*/
-static jlong android_os_SystemClock_currentTimeMicro(JNIEnv* env,
- jobject clazz)
+static jlong android_os_SystemClock_currentTimeMicro()
{
struct timeval tv;
@@ -93,31 +80,22 @@
}
/*
- * public static native long elapsedRealtimeNano();
- */
-static jlong android_os_SystemClock_elapsedRealtimeNano(JNIEnv* env,
- jobject clazz)
-{
- return (jlong)elapsedRealtimeNano();
-}
-
-/*
* JNI registration.
*/
static const JNINativeMethod gMethods[] = {
- /* name, signature, funcPtr */
- { "uptimeMillis", "!()J",
- (void*) android_os_SystemClock_uptimeMillis },
- { "elapsedRealtime", "!()J",
- (void*) android_os_SystemClock_elapsedRealtime },
- { "currentThreadTimeMillis", "!()J",
+ // All of these are @CriticalNative, so we can defer directly to SystemClock.h for
+ // some of these
+ { "uptimeMillis", "()J", (void*) uptimeMillis },
+ { "elapsedRealtime", "()J", (void*) elapsedRealtime },
+ { "elapsedRealtimeNanos", "()J", (void*) elapsedRealtimeNano },
+
+ // SystemClock doesn't have an implementation for these that we can directly call
+ { "currentThreadTimeMillis", "()J",
(void*) android_os_SystemClock_currentThreadTimeMillis },
- { "currentThreadTimeMicro", "!()J",
+ { "currentThreadTimeMicro", "()J",
(void*) android_os_SystemClock_currentThreadTimeMicro },
- { "currentTimeMicro", "!()J",
+ { "currentTimeMicro", "()J",
(void*) android_os_SystemClock_currentTimeMicro },
- { "elapsedRealtimeNanos", "!()J",
- (void*) android_os_SystemClock_elapsedRealtimeNano },
};
int register_android_os_SystemClock(JNIEnv* env)
{
diff --git a/core/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp
index 30fc47b..ea893f0 100644
--- a/core/jni/android_os_Trace.cpp
+++ b/core/jni/android_os_Trace.cpp
@@ -110,27 +110,30 @@
{ "nativeGetEnabledTags",
"()J",
(void*)android_os_Trace_nativeGetEnabledTags },
- { "nativeTraceCounter",
- "!(JLjava/lang/String;I)V",
- (void*)android_os_Trace_nativeTraceCounter },
- { "nativeTraceBegin",
- "!(JLjava/lang/String;)V",
- (void*)android_os_Trace_nativeTraceBegin },
- { "nativeTraceEnd",
- "!(J)V",
- (void*)android_os_Trace_nativeTraceEnd },
- { "nativeAsyncTraceBegin",
- "!(JLjava/lang/String;I)V",
- (void*)android_os_Trace_nativeAsyncTraceBegin },
- { "nativeAsyncTraceEnd",
- "!(JLjava/lang/String;I)V",
- (void*)android_os_Trace_nativeAsyncTraceEnd },
{ "nativeSetAppTracingAllowed",
"(Z)V",
(void*)android_os_Trace_nativeSetAppTracingAllowed },
{ "nativeSetTracingEnabled",
"(Z)V",
(void*)android_os_Trace_nativeSetTracingEnabled },
+
+ // ----------- @FastNative ----------------
+
+ { "nativeTraceCounter",
+ "(JLjava/lang/String;I)V",
+ (void*)android_os_Trace_nativeTraceCounter },
+ { "nativeTraceBegin",
+ "(JLjava/lang/String;)V",
+ (void*)android_os_Trace_nativeTraceBegin },
+ { "nativeTraceEnd",
+ "(J)V",
+ (void*)android_os_Trace_nativeTraceEnd },
+ { "nativeAsyncTraceBegin",
+ "(JLjava/lang/String;I)V",
+ (void*)android_os_Trace_nativeAsyncTraceBegin },
+ { "nativeAsyncTraceEnd",
+ "(JLjava/lang/String;I)V",
+ (void*)android_os_Trace_nativeAsyncTraceEnd },
};
int register_android_os_Trace(JNIEnv* env) {
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index c4c1511..428159a 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -186,18 +186,19 @@
argv[argc++] = AssetManager::TARGET_APK_PATH;
argv[argc++] = AssetManager::IDMAP_DIR;
- // Directories to scan for overlays: if OVERLAY_SUBDIR_PROPERTY is defined,
- // use OVERLAY_SUBDIR/<value of OVERLAY_SUBDIR_PROPERTY>/ if exists, otherwise
+ // Directories to scan for overlays: if OVERLAY_SKU_DIR_PROPERTY is defined,
+ // use OVERLAY_DIR/<value of OVERLAY_SKU_DIR_PROPERTY> if exists, otherwise
// use OVERLAY_DIR if exists.
char subdir[PROP_VALUE_MAX];
- int len = __system_property_get(AssetManager::OVERLAY_SUBDIR_PROPERTY, subdir);
+ int len = __system_property_get(AssetManager::OVERLAY_SKU_DIR_PROPERTY, subdir);
+ String8 overlayPath;
if (len > 0) {
- String8 subdirPath = String8(AssetManager::OVERLAY_SUBDIR) + "/" + subdir;
- if (stat(subdirPath.string(), &st) == 0) {
- argv[argc++] = subdirPath.string();
- }
- } else if (stat(AssetManager::OVERLAY_DIR, &st) == 0) {
- argv[argc++] = AssetManager::OVERLAY_DIR;
+ overlayPath = String8(AssetManager::OVERLAY_DIR) + "/" + subdir;
+ } else {
+ overlayPath = String8(AssetManager::OVERLAY_DIR);
+ }
+ if (stat(overlayPath.string(), &st) == 0) {
+ argv[argc++] = overlayPath.string();
}
// Finally, invoke idmap (if any overlay directory exists)
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index ea5a760..2eada3e 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -161,7 +161,8 @@
{ "nativeDispose",
"(J)V",
(void*)nativeDispose },
- { "nativeScheduleVsync", "!(J)V",
+ // @FastNative
+ { "nativeScheduleVsync", "(J)V",
(void*)nativeScheduleVsync }
};
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 0245d38..2132f3d 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -383,17 +383,6 @@
return 0;
}
-static jlong android_view_MotionEvent_nativeCopy(JNIEnv* env, jclass clazz,
- jlong destNativePtr, jlong sourceNativePtr, jboolean keepHistory) {
- MotionEvent* destEvent = reinterpret_cast<MotionEvent*>(destNativePtr);
- if (!destEvent) {
- destEvent = new MotionEvent();
- }
- MotionEvent* sourceEvent = reinterpret_cast<MotionEvent*>(sourceNativePtr);
- destEvent->copyFrom(sourceEvent, keepHistory);
- return reinterpret_cast<jlong>(destEvent);
-}
-
static void android_view_MotionEvent_nativeDispose(JNIEnv* env, jclass clazz,
jlong nativePtr) {
MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
@@ -426,228 +415,6 @@
event->setMetaState(event->getMetaState() | metaState);
}
-static jint android_view_MotionEvent_nativeGetDeviceId(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->getDeviceId();
-}
-
-static jint android_view_MotionEvent_nativeGetSource(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->getSource();
-}
-
-static void android_view_MotionEvent_nativeSetSource(JNIEnv* env, jclass clazz,
- jlong nativePtr, jint source) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- event->setSource(source);
-}
-
-static jint android_view_MotionEvent_nativeGetAction(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->getAction();
-}
-
-static void android_view_MotionEvent_nativeSetAction(JNIEnv* env, jclass clazz,
- jlong nativePtr, jint action) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- event->setAction(action);
-}
-
-static int android_view_MotionEvent_nativeGetActionButton(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->getActionButton();
-}
-
-static void android_view_MotionEvent_nativeSetActionButton(JNIEnv* env, jclass clazz,
- jlong nativePtr, jint button) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- event->setActionButton(button);
-}
-
-static jboolean android_view_MotionEvent_nativeIsTouchEvent(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->isTouchEvent();
-}
-
-static jint android_view_MotionEvent_nativeGetFlags(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->getFlags();
-}
-
-static void android_view_MotionEvent_nativeSetFlags(JNIEnv* env, jclass clazz,
- jlong nativePtr, jint flags) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- event->setFlags(flags);
-}
-
-static jint android_view_MotionEvent_nativeGetEdgeFlags(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->getEdgeFlags();
-}
-
-static void android_view_MotionEvent_nativeSetEdgeFlags(JNIEnv* env, jclass clazz,
- jlong nativePtr, jint edgeFlags) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- event->setEdgeFlags(edgeFlags);
-}
-
-static jint android_view_MotionEvent_nativeGetMetaState(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->getMetaState();
-}
-
-static jint android_view_MotionEvent_nativeGetButtonState(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->getButtonState();
-}
-
-static void android_view_MotionEvent_nativeSetButtonState(JNIEnv* env, jclass clazz,
- jlong nativePtr, jint buttonState) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- event->setButtonState(buttonState);
-}
-
-static void android_view_MotionEvent_nativeOffsetLocation(JNIEnv* env, jclass clazz,
- jlong nativePtr, jfloat deltaX, jfloat deltaY) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->offsetLocation(deltaX, deltaY);
-}
-
-static jfloat android_view_MotionEvent_nativeGetXOffset(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->getXOffset();
-}
-
-static jfloat android_view_MotionEvent_nativeGetYOffset(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->getYOffset();
-}
-
-static jfloat android_view_MotionEvent_nativeGetXPrecision(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->getXPrecision();
-}
-
-static jfloat android_view_MotionEvent_nativeGetYPrecision(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->getYPrecision();
-}
-
-static jlong android_view_MotionEvent_nativeGetDownTimeNanos(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->getDownTime();
-}
-
-static void android_view_MotionEvent_nativeSetDownTimeNanos(JNIEnv* env, jclass clazz,
- jlong nativePtr, jlong downTimeNanos) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- event->setDownTime(downTimeNanos);
-}
-
-static jint android_view_MotionEvent_nativeGetPointerCount(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return jint(event->getPointerCount());
-}
-
-static jint android_view_MotionEvent_nativeGetPointerId(JNIEnv* env, jclass clazz,
- jlong nativePtr, jint pointerIndex) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- size_t pointerCount = event->getPointerCount();
- if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
- return -1;
- }
- return event->getPointerId(pointerIndex);
-}
-
-static jint android_view_MotionEvent_nativeGetToolType(JNIEnv* env, jclass clazz,
- jlong nativePtr, jint pointerIndex) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- size_t pointerCount = event->getPointerCount();
- if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
- return -1;
- }
- return event->getToolType(pointerIndex);
-}
-
-static jint android_view_MotionEvent_nativeFindPointerIndex(JNIEnv* env, jclass clazz,
- jlong nativePtr, jint pointerId) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return jint(event->findPointerIndex(pointerId));
-}
-
-static jint android_view_MotionEvent_nativeGetHistorySize(JNIEnv* env, jclass clazz,
- jlong nativePtr) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return jint(event->getHistorySize());
-}
-
-static jlong android_view_MotionEvent_nativeGetEventTimeNanos(JNIEnv* env, jclass clazz,
- jlong nativePtr, jint historyPos) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- if (historyPos == HISTORY_CURRENT) {
- return event->getEventTime();
- } else {
- size_t historySize = event->getHistorySize();
- if (!validateHistoryPos(env, historyPos, historySize)) {
- return 0;
- }
- return event->getHistoricalEventTime(historyPos);
- }
-}
-
-static jfloat android_view_MotionEvent_nativeGetRawAxisValue(JNIEnv* env, jclass clazz,
- jlong nativePtr, jint axis, jint pointerIndex, jint historyPos) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- size_t pointerCount = event->getPointerCount();
- if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
- return 0;
- }
-
- if (historyPos == HISTORY_CURRENT) {
- return event->getRawAxisValue(axis, pointerIndex);
- } else {
- size_t historySize = event->getHistorySize();
- if (!validateHistoryPos(env, historyPos, historySize)) {
- return 0;
- }
- return event->getHistoricalRawAxisValue(axis, pointerIndex, historyPos);
- }
-}
-
-static jfloat android_view_MotionEvent_nativeGetAxisValue(JNIEnv* env, jclass clazz,
- jlong nativePtr, jint axis, jint pointerIndex, jint historyPos) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- size_t pointerCount = event->getPointerCount();
- if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
- return 0;
- }
-
- if (historyPos == HISTORY_CURRENT) {
- return event->getAxisValue(axis, pointerIndex);
- } else {
- size_t historySize = event->getHistorySize();
- if (!validateHistoryPos(env, historyPos, historySize)) {
- return 0;
- }
- return event->getHistoricalAxisValue(axis, pointerIndex, historyPos);
- }
-}
-
static void android_view_MotionEvent_nativeGetPointerCoords(JNIEnv* env, jclass clazz,
jlong nativePtr, jint pointerIndex, jint historyPos, jobject outPointerCoordsObj) {
MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
@@ -684,30 +451,6 @@
pointerPropertiesFromNative(env, pointerProperties, outPointerPropertiesObj);
}
-static void android_view_MotionEvent_nativeScale(JNIEnv* env, jclass clazz,
- jlong nativePtr, jfloat scale) {
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- event->scale(scale);
-}
-
-static void android_view_MotionEvent_nativeTransform(JNIEnv* env, jclass clazz,
- jlong nativePtr, jobject matrixObj) {
- SkMatrix* matrix = android_graphics_Matrix_getSkMatrix(env, matrixObj);
- MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-
- float m[9];
- m[0] = SkScalarToFloat(matrix->get(SkMatrix::kMScaleX));
- m[1] = SkScalarToFloat(matrix->get(SkMatrix::kMSkewX));
- m[2] = SkScalarToFloat(matrix->get(SkMatrix::kMTransX));
- m[3] = SkScalarToFloat(matrix->get(SkMatrix::kMSkewY));
- m[4] = SkScalarToFloat(matrix->get(SkMatrix::kMScaleY));
- m[5] = SkScalarToFloat(matrix->get(SkMatrix::kMTransY));
- m[6] = SkScalarToFloat(matrix->get(SkMatrix::kMPersp0));
- m[7] = SkScalarToFloat(matrix->get(SkMatrix::kMPersp1));
- m[8] = SkScalarToFloat(matrix->get(SkMatrix::kMPersp2));
- event->transform(m);
-}
-
static jlong android_view_MotionEvent_nativeReadFromParcel(JNIEnv* env, jclass clazz,
jlong nativePtr, jobject parcelObj) {
MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
@@ -750,6 +493,243 @@
return static_cast<jint>(MotionEvent::getAxisFromLabel(axisLabel.c_str()));
}
+// ---------------- @FastNative ----------------------------------
+
+static jint android_view_MotionEvent_nativeGetPointerId(JNIEnv* env, jclass clazz,
+ jlong nativePtr, jint pointerIndex) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ size_t pointerCount = event->getPointerCount();
+ if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
+ return -1;
+ }
+ return event->getPointerId(pointerIndex);
+}
+
+static jint android_view_MotionEvent_nativeGetToolType(JNIEnv* env, jclass clazz,
+ jlong nativePtr, jint pointerIndex) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ size_t pointerCount = event->getPointerCount();
+ if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
+ return -1;
+ }
+ return event->getToolType(pointerIndex);
+}
+
+static jlong android_view_MotionEvent_nativeGetEventTimeNanos(JNIEnv* env, jclass clazz,
+ jlong nativePtr, jint historyPos) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ if (historyPos == HISTORY_CURRENT) {
+ return event->getEventTime();
+ } else {
+ size_t historySize = event->getHistorySize();
+ if (!validateHistoryPos(env, historyPos, historySize)) {
+ return 0;
+ }
+ return event->getHistoricalEventTime(historyPos);
+ }
+}
+
+static jfloat android_view_MotionEvent_nativeGetRawAxisValue(JNIEnv* env, jclass clazz,
+ jlong nativePtr, jint axis,
+ jint pointerIndex, jint historyPos) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ size_t pointerCount = event->getPointerCount();
+ if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
+ return 0;
+ }
+
+ if (historyPos == HISTORY_CURRENT) {
+ return event->getRawAxisValue(axis, pointerIndex);
+ } else {
+ size_t historySize = event->getHistorySize();
+ if (!validateHistoryPos(env, historyPos, historySize)) {
+ return 0;
+ }
+ return event->getHistoricalRawAxisValue(axis, pointerIndex, historyPos);
+ }
+}
+
+static jfloat android_view_MotionEvent_nativeGetAxisValue(JNIEnv* env, jclass clazz,
+ jlong nativePtr, jint axis, jint pointerIndex, jint historyPos) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ size_t pointerCount = event->getPointerCount();
+ if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
+ return 0;
+ }
+
+ if (historyPos == HISTORY_CURRENT) {
+ return event->getAxisValue(axis, pointerIndex);
+ } else {
+ size_t historySize = event->getHistorySize();
+ if (!validateHistoryPos(env, historyPos, historySize)) {
+ return 0;
+ }
+ return event->getHistoricalAxisValue(axis, pointerIndex, historyPos);
+ }
+}
+
+// ----------------- @CriticalNative ------------------------------
+
+static jlong android_view_MotionEvent_nativeCopy(jlong destNativePtr, jlong sourceNativePtr,
+ jboolean keepHistory) {
+ MotionEvent* destEvent = reinterpret_cast<MotionEvent*>(destNativePtr);
+ if (!destEvent) {
+ destEvent = new MotionEvent();
+ }
+ MotionEvent* sourceEvent = reinterpret_cast<MotionEvent*>(sourceNativePtr);
+ destEvent->copyFrom(sourceEvent, keepHistory);
+ return reinterpret_cast<jlong>(destEvent);
+}
+
+static jint android_view_MotionEvent_nativeGetDeviceId(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getDeviceId();
+}
+
+static jint android_view_MotionEvent_nativeGetSource(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getSource();
+}
+
+static void android_view_MotionEvent_nativeSetSource(jlong nativePtr, jint source) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ event->setSource(source);
+}
+
+static jint android_view_MotionEvent_nativeGetAction(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getAction();
+}
+
+static void android_view_MotionEvent_nativeSetAction(jlong nativePtr, jint action) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ event->setAction(action);
+}
+
+static int android_view_MotionEvent_nativeGetActionButton(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getActionButton();
+}
+
+static void android_view_MotionEvent_nativeSetActionButton(jlong nativePtr, jint button) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ event->setActionButton(button);
+}
+
+static jboolean android_view_MotionEvent_nativeIsTouchEvent(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->isTouchEvent();
+}
+
+static jint android_view_MotionEvent_nativeGetFlags(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getFlags();
+}
+
+static void android_view_MotionEvent_nativeSetFlags(jlong nativePtr, jint flags) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ event->setFlags(flags);
+}
+
+static jint android_view_MotionEvent_nativeGetEdgeFlags(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getEdgeFlags();
+}
+
+static void android_view_MotionEvent_nativeSetEdgeFlags(jlong nativePtr, jint edgeFlags) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ event->setEdgeFlags(edgeFlags);
+}
+
+static jint android_view_MotionEvent_nativeGetMetaState(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getMetaState();
+}
+
+static jint android_view_MotionEvent_nativeGetButtonState(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getButtonState();
+}
+
+static void android_view_MotionEvent_nativeSetButtonState(jlong nativePtr, jint buttonState) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ event->setButtonState(buttonState);
+}
+
+static void android_view_MotionEvent_nativeOffsetLocation(jlong nativePtr, jfloat deltaX,
+ jfloat deltaY) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->offsetLocation(deltaX, deltaY);
+}
+
+static jfloat android_view_MotionEvent_nativeGetXOffset(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getXOffset();
+}
+
+static jfloat android_view_MotionEvent_nativeGetYOffset(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getYOffset();
+}
+
+static jfloat android_view_MotionEvent_nativeGetXPrecision(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getXPrecision();
+}
+
+static jfloat android_view_MotionEvent_nativeGetYPrecision(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getYPrecision();
+}
+
+static jlong android_view_MotionEvent_nativeGetDownTimeNanos(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getDownTime();
+}
+
+static void android_view_MotionEvent_nativeSetDownTimeNanos(jlong nativePtr, jlong downTimeNanos) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ event->setDownTime(downTimeNanos);
+}
+
+static jint android_view_MotionEvent_nativeGetPointerCount(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return jint(event->getPointerCount());
+}
+
+static jint android_view_MotionEvent_nativeFindPointerIndex(jlong nativePtr, jint pointerId) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return jint(event->findPointerIndex(pointerId));
+}
+
+static jint android_view_MotionEvent_nativeGetHistorySize(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return jint(event->getHistorySize());
+}
+
+static void android_view_MotionEvent_nativeScale(jlong nativePtr, jfloat scale) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ event->scale(scale);
+}
+
+static void android_view_MotionEvent_nativeTransform(jlong nativePtr, jlong matrixPtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
+
+ static_assert(SkMatrix::kMScaleX == 0, "SkMatrix unexpected index");
+ static_assert(SkMatrix::kMSkewX == 1, "SkMatrix unexpected index");
+ static_assert(SkMatrix::kMTransX == 2, "SkMatrix unexpected index");
+ static_assert(SkMatrix::kMSkewY == 3, "SkMatrix unexpected index");
+ static_assert(SkMatrix::kMScaleY == 4, "SkMatrix unexpected index");
+ static_assert(SkMatrix::kMTransY == 5, "SkMatrix unexpected index");
+ static_assert(SkMatrix::kMPersp0 == 6, "SkMatrix unexpected index");
+ static_assert(SkMatrix::kMPersp1 == 7, "SkMatrix unexpected index");
+ static_assert(SkMatrix::kMPersp2 == 8, "SkMatrix unexpected index");
+ float m[9];
+ matrix->get9(m);
+ event->transform(m);
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gMotionEventMethods[] = {
@@ -758,117 +738,12 @@
"(JIIIIIIIFFFFJJI[Landroid/view/MotionEvent$PointerProperties;"
"[Landroid/view/MotionEvent$PointerCoords;)J",
(void*)android_view_MotionEvent_nativeInitialize },
- { "nativeCopy",
- "(JJZ)J",
- (void*)android_view_MotionEvent_nativeCopy },
{ "nativeDispose",
"(J)V",
(void*)android_view_MotionEvent_nativeDispose },
{ "nativeAddBatch",
"(JJ[Landroid/view/MotionEvent$PointerCoords;I)V",
(void*)android_view_MotionEvent_nativeAddBatch },
- { "nativeGetDeviceId",
- "!(J)I",
- (void*)android_view_MotionEvent_nativeGetDeviceId },
- { "nativeGetSource",
- "!(J)I",
- (void*)android_view_MotionEvent_nativeGetSource },
- { "nativeSetSource",
- "!(JI)I",
- (void*)android_view_MotionEvent_nativeSetSource },
- { "nativeGetAction",
- "!(J)I",
- (void*)android_view_MotionEvent_nativeGetAction },
- { "nativeSetAction",
- "!(JI)V",
- (void*)android_view_MotionEvent_nativeSetAction },
- { "nativeGetActionButton",
- "!(J)I",
- (void*)android_view_MotionEvent_nativeGetActionButton},
- { "nativeSetActionButton",
- "!(JI)V",
- (void*)android_view_MotionEvent_nativeSetActionButton},
- { "nativeIsTouchEvent",
- "!(J)Z",
- (void*)android_view_MotionEvent_nativeIsTouchEvent },
- { "nativeGetFlags",
- "!(J)I",
- (void*)android_view_MotionEvent_nativeGetFlags },
- { "nativeSetFlags",
- "!(JI)V",
- (void*)android_view_MotionEvent_nativeSetFlags },
- { "nativeGetEdgeFlags",
- "!(J)I",
- (void*)android_view_MotionEvent_nativeGetEdgeFlags },
- { "nativeSetEdgeFlags",
- "!(JI)V",
- (void*)android_view_MotionEvent_nativeSetEdgeFlags },
- { "nativeGetMetaState",
- "!(J)I",
- (void*)android_view_MotionEvent_nativeGetMetaState },
- { "nativeGetButtonState",
- "!(J)I",
- (void*)android_view_MotionEvent_nativeGetButtonState },
- { "nativeSetButtonState",
- "!(JI)V",
- (void*)android_view_MotionEvent_nativeSetButtonState },
- { "nativeOffsetLocation",
- "!(JFF)V",
- (void*)android_view_MotionEvent_nativeOffsetLocation },
- { "nativeGetXOffset",
- "!(J)F",
- (void*)android_view_MotionEvent_nativeGetXOffset },
- { "nativeGetYOffset",
- "!(J)F",
- (void*)android_view_MotionEvent_nativeGetYOffset },
- { "nativeGetXPrecision",
- "!(J)F",
- (void*)android_view_MotionEvent_nativeGetXPrecision },
- { "nativeGetYPrecision",
- "!(J)F",
- (void*)android_view_MotionEvent_nativeGetYPrecision },
- { "nativeGetDownTimeNanos",
- "!(J)J",
- (void*)android_view_MotionEvent_nativeGetDownTimeNanos },
- { "nativeSetDownTimeNanos",
- "!(JJ)V",
- (void*)android_view_MotionEvent_nativeSetDownTimeNanos },
- { "nativeGetPointerCount",
- "!(J)I",
- (void*)android_view_MotionEvent_nativeGetPointerCount },
- { "nativeGetPointerId",
- "!(JI)I",
- (void*)android_view_MotionEvent_nativeGetPointerId },
- { "nativeGetToolType",
- "!(JI)I",
- (void*)android_view_MotionEvent_nativeGetToolType },
- { "nativeFindPointerIndex",
- "!(JI)I",
- (void*)android_view_MotionEvent_nativeFindPointerIndex },
- { "nativeGetHistorySize",
- "!(J)I",
- (void*)android_view_MotionEvent_nativeGetHistorySize },
- { "nativeGetEventTimeNanos",
- "!(JI)J",
- (void*)android_view_MotionEvent_nativeGetEventTimeNanos },
- { "nativeGetRawAxisValue",
- "!(JIII)F",
- (void*)android_view_MotionEvent_nativeGetRawAxisValue },
- { "nativeGetAxisValue",
- "!(JIII)F",
- (void*)android_view_MotionEvent_nativeGetAxisValue },
- { "nativeGetPointerCoords",
- "(JIILandroid/view/MotionEvent$PointerCoords;)V",
- (void*)android_view_MotionEvent_nativeGetPointerCoords },
- { "nativeGetPointerProperties",
- "(JILandroid/view/MotionEvent$PointerProperties;)V",
- (void*)android_view_MotionEvent_nativeGetPointerProperties },
- { "nativeScale",
- "!(JF)V",
- (void*)android_view_MotionEvent_nativeScale },
- { "nativeTransform",
- "(JLandroid/graphics/Matrix;)V",
- (void*)android_view_MotionEvent_nativeTransform },
{ "nativeReadFromParcel",
"(JLandroid/os/Parcel;)J",
(void*)android_view_MotionEvent_nativeReadFromParcel },
@@ -879,6 +754,116 @@
(void*)android_view_MotionEvent_nativeAxisToString },
{ "nativeAxisFromString", "(Ljava/lang/String;)I",
(void*)android_view_MotionEvent_nativeAxisFromString },
+ { "nativeGetPointerProperties",
+ "(JILandroid/view/MotionEvent$PointerProperties;)V",
+ (void*)android_view_MotionEvent_nativeGetPointerProperties },
+ { "nativeGetPointerCoords",
+ "(JIILandroid/view/MotionEvent$PointerCoords;)V",
+ (void*)android_view_MotionEvent_nativeGetPointerCoords },
+
+ // --------------- @FastNative ----------------------
+ { "nativeGetPointerId",
+ "(JI)I",
+ (void*)android_view_MotionEvent_nativeGetPointerId },
+ { "nativeGetToolType",
+ "(JI)I",
+ (void*)android_view_MotionEvent_nativeGetToolType },
+ { "nativeGetEventTimeNanos",
+ "(JI)J",
+ (void*)android_view_MotionEvent_nativeGetEventTimeNanos },
+ { "nativeGetRawAxisValue",
+ "(JIII)F",
+ (void*)android_view_MotionEvent_nativeGetRawAxisValue },
+ { "nativeGetAxisValue",
+ "(JIII)F",
+ (void*)android_view_MotionEvent_nativeGetAxisValue },
+
+ // --------------- @CriticalNative ------------------
+
+ { "nativeCopy",
+ "(JJZ)J",
+ (void*)android_view_MotionEvent_nativeCopy },
+ { "nativeGetDeviceId",
+ "(J)I",
+ (void*)android_view_MotionEvent_nativeGetDeviceId },
+ { "nativeGetSource",
+ "(J)I",
+ (void*)android_view_MotionEvent_nativeGetSource },
+ { "nativeSetSource",
+ "(JI)I",
+ (void*)android_view_MotionEvent_nativeSetSource },
+ { "nativeGetAction",
+ "(J)I",
+ (void*)android_view_MotionEvent_nativeGetAction },
+ { "nativeSetAction",
+ "(JI)V",
+ (void*)android_view_MotionEvent_nativeSetAction },
+ { "nativeGetActionButton",
+ "(J)I",
+ (void*)android_view_MotionEvent_nativeGetActionButton},
+ { "nativeSetActionButton",
+ "(JI)V",
+ (void*)android_view_MotionEvent_nativeSetActionButton},
+ { "nativeIsTouchEvent",
+ "(J)Z",
+ (void*)android_view_MotionEvent_nativeIsTouchEvent },
+ { "nativeGetFlags",
+ "(J)I",
+ (void*)android_view_MotionEvent_nativeGetFlags },
+ { "nativeSetFlags",
+ "(JI)V",
+ (void*)android_view_MotionEvent_nativeSetFlags },
+ { "nativeGetEdgeFlags",
+ "(J)I",
+ (void*)android_view_MotionEvent_nativeGetEdgeFlags },
+ { "nativeSetEdgeFlags",
+ "(JI)V",
+ (void*)android_view_MotionEvent_nativeSetEdgeFlags },
+ { "nativeGetMetaState",
+ "(J)I",
+ (void*)android_view_MotionEvent_nativeGetMetaState },
+ { "nativeGetButtonState",
+ "(J)I",
+ (void*)android_view_MotionEvent_nativeGetButtonState },
+ { "nativeSetButtonState",
+ "(JI)V",
+ (void*)android_view_MotionEvent_nativeSetButtonState },
+ { "nativeOffsetLocation",
+ "(JFF)V",
+ (void*)android_view_MotionEvent_nativeOffsetLocation },
+ { "nativeGetXOffset",
+ "(J)F",
+ (void*)android_view_MotionEvent_nativeGetXOffset },
+ { "nativeGetYOffset",
+ "(J)F",
+ (void*)android_view_MotionEvent_nativeGetYOffset },
+ { "nativeGetXPrecision",
+ "(J)F",
+ (void*)android_view_MotionEvent_nativeGetXPrecision },
+ { "nativeGetYPrecision",
+ "(J)F",
+ (void*)android_view_MotionEvent_nativeGetYPrecision },
+ { "nativeGetDownTimeNanos",
+ "(J)J",
+ (void*)android_view_MotionEvent_nativeGetDownTimeNanos },
+ { "nativeSetDownTimeNanos",
+ "(JJ)V",
+ (void*)android_view_MotionEvent_nativeSetDownTimeNanos },
+ { "nativeGetPointerCount",
+ "(J)I",
+ (void*)android_view_MotionEvent_nativeGetPointerCount },
+ { "nativeFindPointerIndex",
+ "(JI)I",
+ (void*)android_view_MotionEvent_nativeFindPointerIndex },
+ { "nativeGetHistorySize",
+ "(J)I",
+ (void*)android_view_MotionEvent_nativeGetHistorySize },
+ { "nativeScale",
+ "(JF)V",
+ (void*)android_view_MotionEvent_nativeScale },
+ { "nativeTransform",
+ "(JJ)V",
+ (void*)android_view_MotionEvent_nativeTransform },
};
int register_android_view_MotionEvent(JNIEnv* env) {
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 649cc1e..309bb2f 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -954,7 +954,7 @@
const char* const kClassPathName = "android/view/ThreadedRenderer";
static const JNINativeMethod gMethods[] = {
- { "nSupportsOpenGL", "!()Z", (void*) android_view_ThreadedRenderer_supportsOpenGL },
+ { "nSupportsOpenGL", "()Z", (void*) android_view_ThreadedRenderer_supportsOpenGL },
{ "nSetAtlas", "(JLandroid/view/GraphicBuffer;[J)V", (void*) android_view_ThreadedRenderer_setAtlas },
{ "nSetProcessStatsBuffer", "(JI)V", (void*) android_view_ThreadedRenderer_setProcessStatsBuffer },
{ "nGetRenderThreadTid", "(J)I", (void*) android_view_ThreadedRenderer_getRenderThreadTid },
diff --git a/core/jni/fd_utils-inl.h b/core/jni/fd_utils-inl.h
index 5c17b23..af27069 100644
--- a/core/jni/fd_utils-inl.h
+++ b/core/jni/fd_utils-inl.h
@@ -260,16 +260,13 @@
// Whitelist files needed for Runtime Resource Overlay, like these:
// /system/vendor/overlay/framework-res.apk
- // /system/vendor/overlay-subdir/pg/framework-res.apk
+ // /system/vendor/overlay/PG/android-framework-runtime-resource-overlay.apk
// /data/resource-cache/system@vendor@overlay@framework-res.apk@idmap
- // /data/resource-cache/system@vendor@overlay-subdir@pg@framework-res.apk@idmap
- // See AssetManager.cpp for more details on overlay-subdir.
+ // /data/resource-cache/system@vendor@overlay@PG@framework-res.apk@idmap
static const char* kOverlayDir = "/system/vendor/overlay/";
- static const char* kOverlaySubdir = "/system/vendor/overlay-subdir/";
static const char* kApkSuffix = ".apk";
- if ((android::base::StartsWith(path, kOverlayDir)
- || android::base::StartsWith(path, kOverlaySubdir))
+ if (android::base::StartsWith(path, kOverlayDir)
&& android::base::EndsWith(path, kApkSuffix)
&& path.find("/../") == std::string::npos) {
return true;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a08c63e..d4bef17 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -496,6 +496,8 @@
<protected-broadcast android:name="com.android.server.retaildemo.ACTION_RESET_DEMO" />
+ <protected-broadcast android:name="android.intent.action.DEVICE_LOCKED_CHANGED" />
+
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
diff --git a/core/res/res/color/background_cache_hint_selector_device_default.xml b/core/res/res/color/background_cache_hint_selector_device_default.xml
new file mode 100644
index 0000000..3cb4bbc
--- /dev/null
+++ b/core/res/res/color/background_cache_hint_selector_device_default.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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_accelerated="false" android:color="?attr/colorBackground" />
+ <item android:color="@android:color/transparent" />
+</selector>
diff --git a/core/res/res/values-bs-rBA/strings.xml b/core/res/res/values-bs-rBA/strings.xml
index 083a869..7cb46fe 100644
--- a/core/res/res/values-bs-rBA/strings.xml
+++ b/core/res/res/values-bs-rBA/strings.xml
@@ -1357,7 +1357,7 @@
<string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB disk"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB pohrana"</string>
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Uredi"</string>
- <string name="data_usage_warning_title" msgid="3620440638180218181">"Upozorenje o korištenju podataka"</string>
+ <string name="data_usage_warning_title" msgid="3620440638180218181">"Upozorenje o prijenosu podataka"</string>
<string name="data_usage_warning_body" msgid="6660692274311972007">"Dodirnite za prikaz upotrebe i postavki."</string>
<string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Dostignut limit za 2G-3G podatke"</string>
<string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Dostignut limit za 4G podatke"</string>
@@ -1598,7 +1598,7 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Ažurirao administrator"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Izbrisao administrator"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Da bi se trajanje baterije produžilo, opcija za štednju baterije minimizira rad uređaja i ograničava vibriranje, usluge lokacije i većinu prijenosa podataka u pozadini. E-pošta, poruke i druge aplikacije koje se oslanjaju na sinhronizaciju ne mogu biti ažurirane dok ih ne otvorite.\n\nŠtednja baterije se automatski isključi prilikom punjenja uređaja."</string>
- <string name="data_saver_description" msgid="6015391409098303235">"Da bi se smanjilo korištenje podataka, usluga Ušteda podataka sprečava da neke aplikacije šalju ili primaju podatke u pozadini. Aplikacija koju trenutno koristite može pristupiti podacima, ali se to može desiti rjeđe. To može značiti, naprimjer, da se slike ne prikazuju sve dok ih ne dodirnete."</string>
+ <string name="data_saver_description" msgid="6015391409098303235">"Da bi se smanjio prijenos podataka, usluga Ušteda podataka sprečava da neke aplikacije šalju ili primaju podatke u pozadini. Aplikacija koju trenutno koristite može pristupiti podacima, ali se to može desiti rjeđe. To može značiti, naprimjer, da se slike ne prikazuju sve dok ih ne dodirnete."</string>
<string name="data_saver_enable_title" msgid="4674073932722787417">"Uključiti Uštedu podataka?"</string>
<string name="data_saver_enable_button" msgid="7147735965247211818">"Uključi"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml
index a5c3c6e..704667a 100644
--- a/core/res/res/values-gl-rES/strings.xml
+++ b/core/res/res/values-gl-rES/strings.xml
@@ -214,7 +214,7 @@
<string name="global_actions" product="default" msgid="2406416831541615258">"Opcións de teléfono"</string>
<string name="global_action_lock" msgid="2844945191792119712">"Bloqueo da pantalla"</string>
<string name="global_action_power_off" msgid="4471879440839879722">"Apagar"</string>
- <string name="global_action_emergency" msgid="7112311161137421166">"Emerxencia"</string>
+ <string name="global_action_emergency" msgid="7112311161137421166">"Emerxencias"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Informe de erros"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Crear informe de erros"</string>
<string name="bugreport_message" msgid="398447048750350456">"Este informe recompilará información acerca do estado actual do teu dispositivo para enviala en forma de mensaxe de correo electrónico. O informe de erros tardará un pouco en completarse desde o seu inicio ata que estea preparado para enviarse, polo que che recomendamos que teñas paciencia."</string>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index 4eaadfe..2491f82 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -1331,7 +1331,7 @@
<string name="storage_usb_drive_label" msgid="4501418548927759953">"ឧបករណ៍ផ្ទុក USB <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb" msgid="3017954059538517278">"ឧបករណ៍ផ្ទុកយូអេសប៊ី"</string>
<string name="extract_edit_menu_button" msgid="8940478730496610137">"កែសម្រួល"</string>
- <string name="data_usage_warning_title" msgid="3620440638180218181">"ការដាស់តឿនពីការប្រើទិន្នន័យ"</string>
+ <string name="data_usage_warning_title" msgid="3620440638180218181">"ការព្រមានអំពីការប្រើទិន្នន័យ"</string>
<string name="data_usage_warning_body" msgid="6660692274311972007">"ប៉ះដើម្បីមើលការប្រើប្រាស់ និងការកំណត់"</string>
<string name="data_usage_3g_limit_title" msgid="4361523876818447683">"បានដល់ដែនកំណត់ទិន្នន័យ 2G-3G"</string>
<string name="data_usage_4g_limit_title" msgid="4609566827219442376">"បានដល់ដែនកំណត់ទិន្នន័យ 4G"</string>
diff --git a/core/res/res/values-ky-rKG/strings.xml b/core/res/res/values-ky-rKG/strings.xml
index 1a5f348..7a48ae5 100644
--- a/core/res/res/values-ky-rKG/strings.xml
+++ b/core/res/res/values-ky-rKG/strings.xml
@@ -1329,7 +1329,7 @@
<string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB түзмөгү"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB эстутуму"</string>
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Өзгөртүү"</string>
- <string name="data_usage_warning_title" msgid="3620440638180218181">"Дайындарды колдонуу айгайы"</string>
+ <string name="data_usage_warning_title" msgid="3620440638180218181">"Дайындарды колдонууну чектөө"</string>
<string name="data_usage_warning_body" msgid="6660692274311972007">"Колдонулушун жана жөндөөлөрүн көрүү үчүн таптаңыз."</string>
<string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G дайындар чегине жетти"</string>
<string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G дайындар чегине жетти"</string>
diff --git a/core/res/res/values-ml-rIN/strings.xml b/core/res/res/values-ml-rIN/strings.xml
index af47a9a..beb6a67 100644
--- a/core/res/res/values-ml-rIN/strings.xml
+++ b/core/res/res/values-ml-rIN/strings.xml
@@ -1329,7 +1329,7 @@
<string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB ഡ്രൈവ്"</string>
<string name="storage_usb" msgid="3017954059538517278">"USB സ്റ്റോറേജ്"</string>
<string name="extract_edit_menu_button" msgid="8940478730496610137">"എഡിറ്റുചെയ്യുക"</string>
- <string name="data_usage_warning_title" msgid="3620440638180218181">"ഡാറ്റാ ഉപയോഗ അലേർട്ട്"</string>
+ <string name="data_usage_warning_title" msgid="3620440638180218181">"ഡാറ്റാ ഉപയോഗ മുന്നറിയിപ്പ്"</string>
<string name="data_usage_warning_body" msgid="6660692274311972007">"ഉപയോഗവും ക്രമീകരണവും കാണാൻ ടാപ്പുചെയ്യുക."</string>
<string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G ഡാറ്റ പരിധിയിലെത്തി"</string>
<string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G ഡാറ്റ പരിധിയിലെത്തി"</string>
diff --git a/core/res/res/values-mr-rIN/strings.xml b/core/res/res/values-mr-rIN/strings.xml
index f0e437a..758b21b 100644
--- a/core/res/res/values-mr-rIN/strings.xml
+++ b/core/res/res/values-mr-rIN/strings.xml
@@ -1075,7 +1075,7 @@
<string name="network_available_sign_in" msgid="1848877297365446605">"नेटवर्कवर साइन इन करा"</string>
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
<skip />
- <string name="wifi_no_internet" msgid="8451173622563841546">"वाय-फाय मध्ये इंटरनेट प्रवेश नाही"</string>
+ <string name="wifi_no_internet" msgid="8451173622563841546">"वाय-फायवरून इंटरनेटवर प्रवेश नाही"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"पर्यायांसाठी टॅप करा"</string>
<string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> वर स्विच केले"</string>
<string name="network_switch_metered_detail" msgid="5325661434777870353">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> कडे इंटरनेट प्रवेश नसताना डिव्हाइस <xliff:g id="NEW_NETWORK">%1$s</xliff:g> वापरतो. शुल्क लागू शकतील."</string>
diff --git a/core/res/res/values-pa-rIN/strings.xml b/core/res/res/values-pa-rIN/strings.xml
index 87884b7..d3476aa 100644
--- a/core/res/res/values-pa-rIN/strings.xml
+++ b/core/res/res/values-pa-rIN/strings.xml
@@ -363,7 +363,7 @@
<string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"ਐਪ ਨੂੰ ਤੁਹਾਡਾ ਅਨੁਮਾਨਿਤ ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਪ੍ਰਾਪਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਨੈੱਟਵਰਕ ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਸਰੋਤ ਵਰਤਦੇ ਹੋਏ ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਸੇਵਾਵਾਂ ਰਾਹੀਂ ਪ੍ਰਾਪਤ ਕੀਤਾ ਜਾਂਦਾ ਹੈ ਜਿਵੇਂ ਸੈਲ ਟਾਵਰ ਅਤੇ Wi-Fi. ਇਹ ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਸੇਵਾਵਾਂ ਚਾਲੂ ਅਤੇ ਐਪ ਨੂੰ ਉਹਨਾਂ ਨੂੰ ਵਰਤਣ ਲਈ ਤੁਹਾਡੀ ਡੀਵਾਈਸ ਤੇ ਹੋਣੀਆਂ ਚਾਹੀਦੀਆਂ ਹਨ। ਐਪਸ ਇਸਦੀ ਵਰਤੋਂ ਇਹ ਅਨੁਮਾਨ ਲਗਾਉਣ ਲਈ ਕਰ ਸਕਦੇ ਹਨ ਕਿ ਤੁਸੀਂ ਕਿੱਥੇ ਹੋ।"</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ਆਪਣੀਆਂ ਔਡੀਓ ਸੈਟਿੰਗਾਂ ਬਦਲੋ"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ਔਪ ਨੂੰ ਗਲੋਬਲ ਔਡੀਓ ਸੈਟਿੰਗਾਂ ਸੰਸ਼ੋਧਿਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ ਜਿਵੇਂ ਵੌਲਿਊਮ ਅਤੇ ਆਊਟਪੁਟ ਲਈ ਕਿਹੜਾ ਸਪੀਕਰ ਵਰਤਿਆ ਜਾਂਦਾ ਹੈ।"</string>
- <string name="permlab_recordAudio" msgid="3876049771427466323">"ਔਡੀਓ ਰਿਕਾਰਡ ਕਰੋ"</string>
+ <string name="permlab_recordAudio" msgid="3876049771427466323">"ਔਡੀਓ ਰਿਕਾਰਡ ਕਰਨ"</string>
<string name="permdesc_recordAudio" msgid="4906839301087980680">"ਐਪ ਨੂੰ ਮਾਈਕ੍ਰੋਫੋਨ ਨਾਲ ਔਡੀਓ ਰਿਕਾਰਡ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਅਨੁਮਤੀ ਐਪ ਨੂੰ ਤੁਹਾਡੀ ਪੁਸ਼ਟੀ ਤੋਂ ਬਿਨਾਂ ਕਿਸੇ ਵੀ ਸਮੇਂ ਔਡੀਓ ਰਿਕਾਰਡ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦੀ ਹੈ।"</string>
<string name="permlab_sim_communication" msgid="2935852302216852065">"SIM ਨੂੰ ਕਮਾਂਡਾਂ ਭੇਜੋ"</string>
<string name="permdesc_sim_communication" msgid="5725159654279639498">"ਐਪ ਨੂੰ SIM ਨੂੰ ਕਮਾਂਡਾਂ ਭੇਜਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਬਹੁਤ ਘਾਤਕ ਹੈ।"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 84868a2..a7571ef 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -218,7 +218,7 @@
<string name="global_actions" product="default" msgid="2406416831541615258">"Opcje telefonu"</string>
<string name="global_action_lock" msgid="2844945191792119712">"Blokada ekranu"</string>
<string name="global_action_power_off" msgid="4471879440839879722">"Wyłącz"</string>
- <string name="global_action_emergency" msgid="7112311161137421166">"Alarmowe"</string>
+ <string name="global_action_emergency" msgid="7112311161137421166">"Alarmowy"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Zgłoszenie błędu"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Zgłoś błąd"</string>
<string name="bugreport_message" msgid="398447048750350456">"Informacje o bieżącym stanie urządzenia zostaną zebrane i wysłane e-mailem. Przygotowanie zgłoszenia błędu do wysłania chwilę potrwa, więc zachowaj cierpliwość."</string>
diff --git a/core/res/res/values-uz-rUZ/strings.xml b/core/res/res/values-uz-rUZ/strings.xml
index 5c1e5ba..5b7cf74 100644
--- a/core/res/res/values-uz-rUZ/strings.xml
+++ b/core/res/res/values-uz-rUZ/strings.xml
@@ -1099,7 +1099,7 @@
<string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct yoniq"</string>
<string name="wifi_p2p_enabled_notification_message" msgid="8064677407830620023">"Sozlamalarni ochish uchun bosing"</string>
<string name="accept" msgid="1645267259272829559">"Qabul qilish"</string>
- <string name="decline" msgid="2112225451706137894">"Rad qilish"</string>
+ <string name="decline" msgid="2112225451706137894">"Rad etish"</string>
<string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"taklif jo‘natildi"</string>
<string name="wifi_p2p_invitation_to_connect_title" msgid="4958803948658533637">"ulanish taklifi"</string>
<string name="wifi_p2p_from_message" msgid="570389174731951769">"Kimdan:"</string>
@@ -1113,7 +1113,7 @@
<string name="sms_control_title" msgid="7296612781128917719">"SMS xabarlar yuborilmoqda"</string>
<string name="sms_control_message" msgid="3867899169651496433">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> katta miqdordagi SMS xabarlarini jo‘natmoqda. Ushbu ilovaga xabarlar jo‘natishni davom ettirishga ruxsat berasizmi?"</string>
<string name="sms_control_yes" msgid="3663725993855816807">"Ruxsat berish"</string>
- <string name="sms_control_no" msgid="625438561395534982">"Rad qilish"</string>
+ <string name="sms_control_no" msgid="625438561395534982">"Rad etish"</string>
<string name="sms_short_code_confirm_message" msgid="1645436466285310855">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b>ga xabar jo‘natishni xohlaydi."</string>
<string name="sms_short_code_details" msgid="5873295990846059400">"Bunda, mobil hisobingizdan "<b>"to‘lov olinishi mumkin"</b>"."</string>
<string name="sms_premium_short_code_details" msgid="7869234868023975"><b>"Bunda, mobil hisobingizdan to‘lov olinishi mumkin."</b></string>
@@ -1223,7 +1223,7 @@
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Ushbu so‘rovga ruxsat berishni xohlaysizmi?"</string>
<string name="grant_permissions_header_text" msgid="6874497408201826708">"Ruxsat so‘rovi"</string>
<string name="allow" msgid="7225948811296386551">"Ruxsat berish"</string>
- <string name="deny" msgid="2081879885755434506">"Rad qilish"</string>
+ <string name="deny" msgid="2081879885755434506">"Rad etish"</string>
<string name="permission_request_notification_title" msgid="6486759795926237907">"Ruxsat so‘raldi"</string>
<string name="permission_request_notification_with_subtitle" msgid="8530393139639560189">"<xliff:g id="ACCOUNT">%s</xliff:g> hisobi uchun\nruxsat so‘raldi"</string>
<string name="forward_intent_to_owner" msgid="1207197447013960896">"Siz ushbu ilovadan ishchi profilingizdan tashqarida foydalanmoqdasiz"</string>
diff --git a/core/res/res/values-watch/themes_device_defaults.xml b/core/res/res/values-watch/themes_device_defaults.xml
index 2313b26..0471444 100644
--- a/core/res/res/values-watch/themes_device_defaults.xml
+++ b/core/res/res/values-watch/themes_device_defaults.xml
@@ -32,12 +32,12 @@
-->
<resources>
<!-- Theme used for the intent picker activity. -->
- <style name="Theme.DeviceDefault.Resolver" parent="Theme.Material">
+ <style name="Theme.DeviceDefault.Resolver" parent="Theme.DeviceDefault">
<item name="colorControlActivated">?attr/colorControlHighlight</item>
<item name="listPreferredItemPaddingStart">?attr/dialogPreferredPadding</item>
<item name="listPreferredItemPaddingEnd">?attr/dialogPreferredPadding</item>
</style>
<!-- Use a dark theme for watches. -->
- <style name="Theme.DeviceDefault.System" parent="Theme.Material" />
+ <style name="Theme.DeviceDefault.System" />
</resources>
diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml
index 27ee27b..171f5cb 100644
--- a/core/res/res/values/colors_device_defaults.xml
+++ b/core/res/res/values/colors_device_defaults.xml
@@ -32,4 +32,11 @@
<color name="accent_device_default_light">@color/accent_material_light</color>
<color name="accent_device_default_dark">@color/accent_material_dark</color>
<color name="accent_device_default_50">@color/material_deep_teal_50</color>
+
+ <color name="background_device_default_dark">@color/background_material_dark</color>
+ <color name="background_device_default_light">@color/background_material_light</color>
+ <color name="background_floating_device_default_dark">@color/background_floating_material_dark</color>
+ <color name="background_floating_device_default_light">@color/background_floating_material_light</color>
+ <color name="button_normal_device_default_dark">@color/btn_default_material_dark</color>
+ <color name="button_normal_device_default_light">@color/btn_default_material_light</color>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 85838ed..3d1acdf5 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2274,21 +2274,6 @@
<!-- Sprint need a 70 ms delay for 3way call -->
<integer name="config_cdma_3waycall_flash_delay">0</integer>
- <!-- If there is no preload VM number in the sim card, carriers such as
- Verizon require to load a default vm number from the configurantion.
- Define config_default_vm_number for this purpose. And there are two
- optional formats for this configuration as below:
- (1)<item>voicemail number</item>
- (2)<item>voicemail number;gid</item>
- The logic to pick up the correct voicemail number:
- (1) If the config_default_vm_number array has no gid special item, the last one will be
- picked
- (2) If the config_default_vm_number array has gid special item and it matches the current
- sim's gid, it will be picked.
- (3) If the config_default_vm_number array has gid special item but it doesn't match the
- current sim's gid, the last one without gid will be picked -->
- <string-array translatable="false" name="config_default_vm_number" />
-
<!--SIM does not save, but the voice mail number to be changed. -->
<bool name="editable_voicemailnumber">false</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 62997f7..019a5e8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2266,7 +2266,6 @@
<java-symbol type="attr" name="closeItemLayout" />
<java-symbol type="layout" name="resolver_different_item_header" />
<java-symbol type="integer" name="config_cdma_3waycall_flash_delay"/>
- <java-symbol type="array" name="config_default_vm_number" />
<java-symbol type="attr" name="windowBackgroundFallback" />
<java-symbol type="id" name="textSpacerNoButtons" />
<java-symbol type="array" name="dial_string_replace" />
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 0e98ade..4be45e9 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -203,45 +203,42 @@
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
-
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+ <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
+ <item name="colorButtonNormal">@color/button_normal_device_default_dark</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with no action bar -->
- <style name="Theme.DeviceDefault.NoActionBar" parent="Theme.Material.NoActionBar">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_dark</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
+ <style name="Theme.DeviceDefault.NoActionBar">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar. This theme
sets {@link android.R.attr#windowFullscreen} to true. -->
- <style name="Theme.DeviceDefault.NoActionBar.Fullscreen" parent="Theme.Material.NoActionBar.Fullscreen">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_dark</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
+ <style name="Theme.DeviceDefault.NoActionBar.Fullscreen">
+ <item name="windowFullscreen">true</item>
+ <item name="windowContentOverlay">@null</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar and
extending in to overscan region. This theme
sets {@link android.R.attr#windowFullscreen} and {@link android.R.attr#windowOverscan}
to true. -->
- <style name="Theme.DeviceDefault.NoActionBar.Overscan" parent="Theme.Material.NoActionBar.Overscan">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_dark</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
+ <style name="Theme.DeviceDefault.NoActionBar.Overscan">
+ <item name="windowFullscreen">true</item>
+ <item name="windowOverscan">true</item>
+ <item name="windowContentOverlay">@null</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} that has no title bar and translucent
system decor. This theme sets {@link android.R.attr#windowTranslucentStatus} and
{@link android.R.attr#windowTranslucentNavigation} to true. -->
- <style name="Theme.DeviceDefault.NoActionBar.TranslucentDecor" parent="Theme.Material.NoActionBar.TranslucentDecor">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_dark</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
+ <style name="Theme.DeviceDefault.NoActionBar.TranslucentDecor">
+ <item name="windowTranslucentStatus">true</item>
+ <item name="windowTranslucentNavigation">true</item>
+ <item name="windowContentOverlay">@null</item>
</style>
<!-- DeviceDefault theme for dialog windows and activities. This changes the window to be
@@ -261,32 +258,30 @@
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
+ <item name="colorBackground">@color/background_floating_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+ <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
+ <item name="colorButtonNormal">@color/button_normal_device_default_dark</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog} that has a nice minimum width for a
regular dialog. -->
- <style name="Theme.DeviceDefault.Dialog.MinWidth" parent="Theme.Material.Dialog.MinWidth">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_dark</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
+ <style name="Theme.DeviceDefault.Dialog.MinWidth">
+ <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
+ <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog} without an action bar -->
- <style name="Theme.DeviceDefault.Dialog.NoActionBar" parent="Theme.Material.Dialog.NoActionBar">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_dark</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
+ <style name="Theme.DeviceDefault.Dialog.NoActionBar">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog_NoActionBar} that has a nice minimum width
for a regular dialog. -->
- <style name="Theme.DeviceDefault.Dialog.NoActionBar.MinWidth" parent="Theme.Material.Dialog.NoActionBar.MinWidth">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_dark</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
+ <style name="Theme.DeviceDefault.Dialog.NoActionBar.MinWidth">
+ <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
+ <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
</style>
<!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -307,92 +302,68 @@
<!-- DeviceDefault theme for a window that will be displayed either full-screen on smaller
screens (small, normal) or as a dialog on larger screens (large, xlarge). -->
- <style name="Theme.DeviceDefault.DialogWhenLarge" parent="Theme.Material.DialogWhenLarge">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_dark</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
- </style>
+ <style name="Theme.DeviceDefault.DialogWhenLarge" parent="@style/Theme.DeviceDefault" />
<!-- DeviceDefault theme for a window without an action bar that will be displayed either
full-screen on smaller screens (small, normal) or as a dialog on larger screens (large,
xlarge). -->
- <style name="Theme.DeviceDefault.DialogWhenLarge.NoActionBar" parent="Theme.Material.DialogWhenLarge.NoActionBar">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_dark</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
- </style>
+ <style name="Theme.DeviceDefault.DialogWhenLarge.NoActionBar" parent="@style/Theme.DeviceDefault.NoActionBar" />
<!-- DeviceDefault theme for a presentation window on a secondary display. -->
- <style name="Theme.DeviceDefault.Dialog.Presentation" parent="Theme.Material.Dialog.Presentation">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_dark</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
- </style>
+ <style name="Theme.DeviceDefault.Dialog.Presentation" parent="@style/Theme.DeviceDefault.NoActionBar.Fullscreen" />
<!-- DeviceDefault theme for panel windows. This removes all extraneous window
decorations, so you basically have an empty rectangle in which to place your content. It makes
the window floating, with a transparent background, and turns off dimming behind the window. -->
- <style name="Theme.DeviceDefault.Panel" parent="Theme.Material.Panel">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_dark</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
+ <style name="Theme.DeviceDefault.Panel">
+ <item name="windowBackground">@color/transparent</item>
+ <item name="colorBackgroundCacheHint">@null</item>
+ <item name="windowFrame">@null</item>
+ <item name="windowContentOverlay">@null</item>
+ <item name="windowAnimationStyle">@null</item>
+ <item name="windowIsFloating">true</item>
+ <item name="backgroundDimEnabled">false</item>
+ <item name="windowIsTranslucent">true</item>
+ <item name="windowNoTitle">true</item>
</style>
<!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
behind them. -->
- <style name="Theme.DeviceDefault.Wallpaper" parent="Theme.Material.Wallpaper">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_dark</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
+ <style name="Theme.DeviceDefault.Wallpaper">
+ <item name="windowBackground">@color/transparent</item>
+ <item name="colorBackgroundCacheHint">@null</item>
+ <item name="windowShowWallpaper">true</item>
</style>
<!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
behind them and without an action bar. -->
- <style name="Theme.DeviceDefault.Wallpaper.NoTitleBar" parent="Theme.Material.Wallpaper.NoTitleBar">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_dark</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
+ <style name="Theme.DeviceDefault.Wallpaper.NoTitleBar">
+ <item name="windowNoTitle">true</item>
</style>
<!-- DeviceDefault style for input methods, which is used by the
{@link android.inputmethodservice.InputMethodService} class.-->
- <style name="Theme.DeviceDefault.InputMethod" parent="Theme.Material.InputMethod">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
+ <style name="Theme.DeviceDefault.InputMethod">
+ <item name="windowAnimationStyle">@style/Animation.InputMethod</item>
+ <item name="imeFullscreenBackground">@drawable/screen_background_selector_light</item>
+ <item name="imeExtractEnterAnimation">@anim/input_method_extract_enter</item>
+ <item name="imeExtractExitAnimation">@anim/input_method_extract_exit</item>
</style>
<!-- DeviceDefault style for input methods, which is used by the
{@link android.service.voice.VoiceInteractionSession} class.-->
<style name="Theme.DeviceDefault.VoiceInteractionSession" parent="Theme.Material.VoiceInteractionSession">
- <!-- Color palette -->
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
</style>
- <style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Material.Dialog.Alert">
- <item name="windowTitleStyle">@style/DialogWindowTitle.DeviceDefault</item>
-
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_dark</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
+ <style name="Theme.DeviceDefault.Dialog.Alert">
+ <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
+ <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
</style>
- <style name="Theme.DeviceDefault.SearchBar" parent="Theme.Material.SearchBar">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_dark</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
- </style>
+ <style name="Theme.DeviceDefault.SearchBar" parent="Theme.DeviceDefault.Panel"/>
<style name="Theme.DeviceDefault.Dialog.NoFrame" parent="Theme.Material.Dialog.NoFrame">
<!-- Color palette -->
@@ -551,53 +522,59 @@
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_light</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
+ <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
+ <item name="colorButtonNormal">@color/button_normal_device_default_light</item>
</style>
<!-- Variant of the DeviceDefault (light) theme that has a solid (opaque) action bar with an
inverse color profile. -->
- <style name="Theme.DeviceDefault.Light.DarkActionBar" parent="Theme.Material.Light.DarkActionBar">
+ <style name="Theme.DeviceDefault.Light.DarkActionBar">
+ <item name="actionBarWidgetTheme">@null</item>
+ <item name="actionBarTheme">@style/ThemeOverlay.Material.Dark.ActionBar</item>
+ <item name="popupTheme">@style/ThemeOverlay.Material.Light</item>
+
<!-- Color palette -->
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+ <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
+ <item name="colorButtonNormal">@color/button_normal_device_default_dark</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar -->
- <style name="Theme.DeviceDefault.Light.NoActionBar" parent="Theme.Material.Light.NoActionBar">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
+ <style name="Theme.DeviceDefault.Light.NoActionBar">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar.
This theme sets {@link android.R.attr#windowFullscreen} to true. -->
- <style name="Theme.DeviceDefault.Light.NoActionBar.Fullscreen" parent="Theme.Material.Light.NoActionBar.Fullscreen">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
+ <style name="Theme.DeviceDefault.Light.NoActionBar.Fullscreen">
+ <item name="windowFullscreen">true</item>
+ <item name="windowContentOverlay">@null</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar
and extending in to overscan region. This theme
sets {@link android.R.attr#windowFullscreen} and {@link android.R.attr#windowOverscan}
to true. -->
- <style name="Theme.DeviceDefault.Light.NoActionBar.Overscan" parent="Theme.Material.Light.NoActionBar.Overscan">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
+ <style name="Theme.DeviceDefault.Light.NoActionBar.Overscan">
+ <item name="windowFullscreen">true</item>
+ <item name="windowOverscan">true</item>
+ <item name="windowContentOverlay">@null</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} that has no title bar and translucent
system decor. This theme sets {@link android.R.attr#windowTranslucentStatus} and
{@link android.R.attr#windowTranslucentNavigation} to true. -->
- <style name="Theme.DeviceDefault.Light.NoActionBar.TranslucentDecor" parent="Theme.Material.Light.NoActionBar.TranslucentDecor">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
+ <style name="Theme.DeviceDefault.Light.NoActionBar.TranslucentDecor">
+ <item name="windowTranslucentStatus">true</item>
+ <item name="windowTranslucentNavigation">true</item>
+ <item name="windowContentOverlay">@null</item>
</style>
<!-- DeviceDefault light theme for dialog windows and activities. This changes the window to be
@@ -617,32 +594,30 @@
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+ <item name="colorBackground">@color/background_device_default_dark</item>
+ <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+ <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
+ <item name="colorButtonNormal">@color/button_normal_device_default_light</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} that has a nice minimum width for a
regular dialog. -->
- <style name="Theme.DeviceDefault.Light.Dialog.MinWidth" parent="Theme.Material.Light.Dialog.MinWidth">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
+ <style name="Theme.DeviceDefault.Light.Dialog.MinWidth">
+ <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
+ <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} without an action bar -->
- <style name="Theme.DeviceDefault.Light.Dialog.NoActionBar" parent="Theme.Material.Light.Dialog.NoActionBar">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
+ <style name="Theme.DeviceDefault.Light.Dialog.NoActionBar">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog_NoActionBar} that has a nice minimum
width for a regular dialog. -->
- <style name="Theme.DeviceDefault.Light.Dialog.NoActionBar.MinWidth" parent="Theme.Material.Light.Dialog.NoActionBar.MinWidth">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
+ <style name="Theme.DeviceDefault.Light.Dialog.NoActionBar.MinWidth">
+ <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
+ <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
</style>
<!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -651,11 +626,6 @@
<item name="windowFixedWidthMinor">@dimen/dialog_fixed_width_minor</item>
<item name="windowFixedHeightMajor">@dimen/dialog_fixed_height_major</item>
<item name="windowFixedHeightMinor">@dimen/dialog_fixed_height_minor</item>
-
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
</style>
<!-- Variant of Theme.DeviceDefault.Dialog.NoActionBar that has a fixed size. -->
@@ -664,128 +634,78 @@
<item name="windowFixedWidthMinor">@dimen/dialog_fixed_width_minor</item>
<item name="windowFixedHeightMajor">@dimen/dialog_fixed_height_major</item>
<item name="windowFixedHeightMinor">@dimen/dialog_fixed_height_minor</item>
-
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
</style>
<!-- DeviceDefault light theme for a window that will be displayed either full-screen on smaller
screens (small, normal) or as a dialog on larger screens (large, xlarge). -->
- <style name="Theme.DeviceDefault.Light.DialogWhenLarge" parent="Theme.Material.Light.DialogWhenLarge">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
- </style>
+ <style name="Theme.DeviceDefault.Light.DialogWhenLarge" parent="@style/Theme.DeviceDefault.Light" />
<!-- DeviceDefault light theme for a window without an action bar that will be displayed either
full-screen on smaller screens (small, normal) or as a dialog on larger screens (large,
xlarge). -->
- <style name="Theme.DeviceDefault.Light.DialogWhenLarge.NoActionBar" parent="Theme.Material.Light.DialogWhenLarge.NoActionBar">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
- </style>
+ <style name="Theme.DeviceDefault.Light.DialogWhenLarge.NoActionBar" parent="@style/Theme.DeviceDefault.Light.NoActionBar" />
<!-- DeviceDefault light theme for a presentation window on a secondary display. -->
- <style name="Theme.DeviceDefault.Light.Dialog.Presentation" parent="Theme.Material.Light.Dialog.Presentation">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
- </style>
+ <style name="Theme.DeviceDefault.Light.Dialog.Presentation" parent="@style/Theme.DeviceDefault.Light.NoActionBar.Fullscreen" />
<!-- DeviceDefault light theme for panel windows. This removes all extraneous window
decorations, so you basically have an empty rectangle in which to place your content. It makes
the window floating, with a transparent background, and turns off dimming behind the window. -->
<style name="Theme.DeviceDefault.Light.Panel" parent="Theme.Material.Light.Panel">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
+ <item name="windowBackground">@color/transparent</item>
+ <item name="colorBackgroundCacheHint">@null</item>
+ <item name="windowFrame">@null</item>
+ <item name="windowContentOverlay">@null</item>
+ <item name="windowAnimationStyle">@null</item>
+ <item name="windowIsFloating">true</item>
+ <item name="backgroundDimEnabled">false</item>
+ <item name="windowIsTranslucent">true</item>
+ <item name="windowNoTitle">true</item>
</style>
- <style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.Alert">
- <item name="windowTitleStyle">@style/DialogWindowTitle.DeviceDefault.Light</item>
-
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
+ <style name="Theme.DeviceDefault.Light.Dialog.Alert">
+ <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
+ <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
</style>
- <style name="Theme.DeviceDefault.Light.SearchBar" parent="Theme.Material.Light.SearchBar">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
- </style>
+ <style name="Theme.DeviceDefault.Light.SearchBar" parent="@style/Theme.DeviceDefault.Light.Panel"/>
- <style name="Theme.DeviceDefault.Light.Voice" parent="Theme.Material.Light.Voice">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
+ <style name="Theme.DeviceDefault.Light.Voice" parent="@style/Theme.DeviceDefault.Light.Dialog">
+ <item name="windowAnimationStyle">@style/Animation.VoiceActivity</item>
+ <item name="backgroundDimEnabled">false</item>
</style>
<!-- DeviceDefault theme for a window that should look like the Settings app. -->
- <style name="Theme.DeviceDefault.Settings" parent="Theme.Material.Settings">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_settings</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
- <item name="colorSecondary">@color/secondary_device_default_settings</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
+ <style name="Theme.DeviceDefault.Settings" parent="Theme.DeviceDefault.Light.DarkActionBar">
+ <item name="presentationTheme">@style/Theme.Material.Settings.Dialog.Presentation</item>
+ <item name="searchDialogTheme">@style/Theme.Material.Settings.SearchBar</item>
+ <item name="panelMenuListTheme">@style/Theme.Material.Settings.CompactMenu</item>
</style>
<!-- DeviceDefault theme for a window that should use Settings theme colors but has
a full dark palette (instead of Light with dark action bar like
Theme.DeviceDefault.Settings. -->
- <style name="Theme.DeviceDefault.Settings.Dark" parent="Theme.Material">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_settings</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
- <item name="colorSecondary">@color/secondary_device_default_settings</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
- </style>
+ <style name="Theme.DeviceDefault.Settings.Dark" parent="Theme.DeviceDefault"/>
<!-- Variant of {@link #Theme_DeviceDefault_Settings_Dark} with no action bar -->
- <style name="Theme.DeviceDefault.Settings.Dark.NoActionBar" parent="Theme.Material.NoActionBar">
- <!-- Color palette -->
+ <style name="Theme.DeviceDefault.Settings.Dark.NoActionBar" parent="Theme.DeviceDefault.NoActionBar"/>
+
+ <style name="Theme.DeviceDefault.Settings.Dialog" parent="Theme.DeviceDefault.Light.Dialog">
<item name="colorPrimary">@color/primary_device_default_settings</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
<item name="colorSecondary">@color/secondary_device_default_settings</item>
- <item name="colorAccent">@color/accent_device_default_dark</item>
</style>
- <style name="Theme.DeviceDefault.Settings.Dialog" parent="Theme.Material.Settings.Dialog">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_settings</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
- <item name="colorSecondary">@color/secondary_device_default_settings</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
- </style>
+ <style name="Theme.DeviceDefault.Settings.DialogWhenLarge" parent="Theme.DeviceDefault.Light"/>
- <style name="Theme.DeviceDefault.Settings.DialogWhenLarge" parent="Theme.Material.Settings.DialogWhenLarge">
- <!-- Color palette -->
+ <style name="Theme.DeviceDefault.Settings.Dialog.Alert" parent="Theme.DeviceDefault.Light.Dialog.Alert">
<item name="colorPrimary">@color/primary_device_default_settings</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
<item name="colorSecondary">@color/secondary_device_default_settings</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
- </style>
-
- <style name="Theme.DeviceDefault.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.Alert">
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_settings</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
- <item name="colorSecondary">@color/secondary_device_default_settings</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
</style>
<!-- Theme used for the intent picker activity. -->
- <style name="Theme.DeviceDefault.Resolver" parent="Theme.Material.Light">
+ <style name="Theme.DeviceDefault.Resolver" parent="Theme.DeviceDefault.Light">
<item name="windowIsTranslucent">true</item>
<item name="windowNoTitle">true</item>
<item name="windowBackground">@color/transparent</item>
@@ -795,11 +715,6 @@
<item name="colorControlActivated">?attr/colorControlHighlight</item>
<item name="listPreferredItemPaddingStart">?attr/dialogPreferredPadding</item>
<item name="listPreferredItemPaddingEnd">?attr/dialogPreferredPadding</item>
-
- <!-- Color palette -->
- <item name="colorPrimary">@color/primary_device_default_light</item>
- <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
- <item name="colorAccent">@color/accent_device_default_light</item>
</style>
<!-- DeviceDefault theme for the default system theme. -->
diff --git a/core/tests/benchmarks/src/android/os/ParcelBenchmark.java b/core/tests/benchmarks/src/android/os/ParcelBenchmark.java
index 4bd2d00..6c45ae8 100644
--- a/core/tests/benchmarks/src/android/os/ParcelBenchmark.java
+++ b/core/tests/benchmarks/src/android/os/ParcelBenchmark.java
@@ -20,12 +20,15 @@
import com.google.caliper.BeforeExperiment;
public class ParcelBenchmark {
+ private static final int INNER_REPS = 1000;
private Parcel mParcel;
@BeforeExperiment
protected void setUp() {
mParcel = Parcel.obtain();
+ mParcel.setDataPosition(0);
+ mParcel.setDataCapacity(INNER_REPS * 8);
}
@AfterExperiment
@@ -36,43 +39,58 @@
public void timeWriteByte(int reps) {
final byte val = 0xF;
- for (int i = 0; i < reps; i++) {
- mParcel.writeByte(val);
+ for (int i = 0; i < (reps / INNER_REPS); i++) {
+ mParcel.setDataPosition(0);
+ for (int j = 0; j < INNER_REPS; j++) {
+ mParcel.writeByte(val);
+ }
}
}
public void timeReadByte(int reps) {
- mParcel.setDataCapacity(reps);
- for (int i = 0; i < reps; i++) {
- mParcel.readByte();
+ for (int i = 0; i < (reps / INNER_REPS); i++) {
+ mParcel.setDataPosition(0);
+ for (int j = 0; j < INNER_REPS; j++) {
+ mParcel.readByte();
+ }
}
}
public void timeWriteInt(int reps) {
final int val = 0xF;
- for (int i = 0; i < reps; i++) {
- mParcel.writeInt(val);
+ for (int i = 0; i < (reps / INNER_REPS); i++) {
+ mParcel.setDataPosition(0);
+ for (int j = 0; j < INNER_REPS; j++) {
+ mParcel.writeInt(val);
+ }
}
}
public void timeReadInt(int reps) {
- mParcel.setDataCapacity(reps << 2);
- for (int i = 0; i < reps; i++) {
- mParcel.readInt();
+ for (int i = 0; i < (reps / INNER_REPS); i++) {
+ mParcel.setDataPosition(0);
+ for (int j = 0; j < INNER_REPS; j++) {
+ mParcel.readInt();
+ }
}
}
public void timeWriteLong(int reps) {
final long val = 0xF;
- for (int i = 0; i < reps; i++) {
- mParcel.writeLong(val);
+ for (int i = 0; i < (reps / INNER_REPS); i++) {
+ mParcel.setDataPosition(0);
+ for (int j = 0; j < INNER_REPS; j++) {
+ mParcel.writeLong(val);
+ }
}
}
public void timeReadLong(int reps) {
- mParcel.setDataCapacity(reps << 3);
- for (int i = 0; i < reps; i++) {
- mParcel.readLong();
+ for (int i = 0; i < (reps / INNER_REPS); i++) {
+ mParcel.setDataPosition(0);
+ for (int j = 0; j < INNER_REPS; j++) {
+ mParcel.readLong();
+ }
}
}
}
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index ee78613..ba1a55d 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1323,17 +1323,6 @@
</meta-data>
</service>
- <activity
- android:name="android.print.mockservice.SettingsActivity"
- android:permission="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY"
- android:exported="true">
- </activity>
-
- <activity
- android:name="android.print.mockservice.AddPrintersActivity"
- android:exported="true">
- </activity>
-
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/core/tests/coretests/res/xml/printservice.xml b/core/tests/coretests/res/xml/printservice.xml
index b105a0f..3bd2b04 100644
--- a/core/tests/coretests/res/xml/printservice.xml
+++ b/core/tests/coretests/res/xml/printservice.xml
@@ -16,6 +16,4 @@
limitations under the License.
-->
-<print-service xmlns:android="http://schemas.android.com/apk/res/android"
- android:settingsActivity="android.print.mockservice.SettingsActivity"
- android:addPrintersActivity="android.print.mockservice.AddPrintersActivity" />
+<print-service xmlns:android="http://schemas.android.com/apk/res/android" />
diff --git a/core/tests/coretests/src/android/print/WorkflowTest.java b/core/tests/coretests/src/android/print/WorkflowTest.java
deleted file mode 100644
index 35cfe22..0000000
--- a/core/tests/coretests/src/android/print/WorkflowTest.java
+++ /dev/null
@@ -1,391 +0,0 @@
-/*
- * Copyright (C) 2016 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.print;
-
-import android.graphics.pdf.PdfDocument;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.os.ParcelFileDescriptor;
-import android.print.mockservice.AddPrintersActivity;
-import android.print.mockservice.MockPrintService;
-
-import android.print.mockservice.PrinterDiscoverySessionCallbacks;
-import android.print.mockservice.StubbablePrinterDiscoverySession;
-import android.print.pdf.PrintedPdfDocument;
-import android.support.test.filters.LargeTest;
-import android.support.test.uiautomator.UiObject;
-import android.support.test.uiautomator.UiObjectNotFoundException;
-import android.support.test.uiautomator.UiSelector;
-import android.util.Log;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-import java.util.function.Supplier;
-
-import static org.junit.Assert.assertEquals;
-
-/**
- * Tests for the basic printing workflows
- */
-public class WorkflowTest extends BasePrintTest {
- private static final String LOG_TAG = WorkflowTest.class.getSimpleName();
-
- private static float sWindowAnimationScaleBefore;
- private static float sTransitionAnimationScaleBefore;
- private static float sAnimatiorDurationScaleBefore;
-
- interface InterruptableConsumer<T> {
- void accept(T t) throws InterruptedException;
- }
-
- /**
- * Execute {@code waiter} until {@code condition} is met.
- *
- * @param condition Conditions to wait for
- * @param waiter Code to execute while waiting
- */
- private void waitWithTimeout(Supplier<Boolean> condition, InterruptableConsumer<Long> waiter)
- throws TimeoutException, InterruptedException {
- long startTime = System.currentTimeMillis();
- while (condition.get()) {
- long timeLeft = OPERATION_TIMEOUT - (System.currentTimeMillis() - startTime);
- if (timeLeft < 0) {
- throw new TimeoutException();
- }
-
- waiter.accept(timeLeft);
- }
- }
-
- /**
- * Executes a shell command using shell user identity, and return the standard output in
- * string.
- *
- * @param cmd the command to run
- *
- * @return the standard output of the command
- */
- private static String runShellCommand(String cmd) throws IOException {
- try (FileInputStream is = new ParcelFileDescriptor.AutoCloseInputStream(
- getInstrumentation().getUiAutomation().executeShellCommand(cmd))) {
- byte[] buf = new byte[64];
- int bytesRead;
-
- StringBuilder stdout = new StringBuilder();
- while ((bytesRead = is.read(buf)) != -1) {
- stdout.append(new String(buf, 0, bytesRead));
- }
-
- return stdout.toString();
- }
- }
-
- @BeforeClass
- public static void disableAnimations() throws Exception {
- try {
- sWindowAnimationScaleBefore = Float.parseFloat(runShellCommand(
- "settings get global window_animation_scale"));
-
- runShellCommand("settings put global window_animation_scale 0");
- } catch (NumberFormatException e) {
- sWindowAnimationScaleBefore = Float.NaN;
- }
- try {
- sTransitionAnimationScaleBefore = Float.parseFloat(runShellCommand(
- "settings get global transition_animation_scale"));
-
- runShellCommand("settings put global transition_animation_scale 0");
- } catch (NumberFormatException e) {
- sTransitionAnimationScaleBefore = Float.NaN;
- }
- try {
- sAnimatiorDurationScaleBefore = Float.parseFloat(runShellCommand(
- "settings get global animator_duration_scale"));
-
- runShellCommand("settings put global animator_duration_scale 0");
- } catch (NumberFormatException e) {
- sAnimatiorDurationScaleBefore = Float.NaN;
- }
- }
-
- @AfterClass
- public static void enableAnimations() throws Exception {
- if (sWindowAnimationScaleBefore != Float.NaN) {
- runShellCommand(
- "settings put global window_animation_scale " + sWindowAnimationScaleBefore);
- }
- if (sTransitionAnimationScaleBefore != Float.NaN) {
- runShellCommand(
- "settings put global transition_animation_scale " +
- sTransitionAnimationScaleBefore);
- }
- if (sAnimatiorDurationScaleBefore != Float.NaN) {
- runShellCommand(
- "settings put global animator_duration_scale " + sAnimatiorDurationScaleBefore);
- }
- }
-
- /** Add a printer with a given name and supported mediasize to a session */
- private void addPrinter(StubbablePrinterDiscoverySession session,
- String name, PrintAttributes.MediaSize mediaSize) {
- PrinterId printerId = session.getService().generatePrinterId(name);
- List<PrinterInfo> printers = new ArrayList<>(1);
-
- PrinterCapabilitiesInfo.Builder builder =
- new PrinterCapabilitiesInfo.Builder(printerId);
-
- builder.setMinMargins(new PrintAttributes.Margins(0, 0, 0, 0))
- .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
- PrintAttributes.COLOR_MODE_COLOR)
- .addMediaSize(mediaSize, true)
- .addResolution(new PrintAttributes.Resolution("300x300", "300x300", 300, 300),
- true);
-
- printers.add(new PrinterInfo.Builder(printerId, name,
- PrinterInfo.STATUS_IDLE).setCapabilities(builder.build()).build());
-
- session.addPrinters(printers);
- }
-
- /** Find a certain element in the UI and click on it */
- private void clickOn(UiSelector selector) throws UiObjectNotFoundException {
- Log.i(LOG_TAG, "Click on " + selector);
- UiObject view = getUiDevice().findObject(selector);
- view.click();
- getUiDevice().waitForIdle();
- }
-
- /** Find a certain text in the UI and click on it */
- private void clickOnText(String text) throws UiObjectNotFoundException {
- clickOn(new UiSelector().text(text));
- }
-
- /** Set the printer in the print activity */
- private void setPrinter(String printerName) throws UiObjectNotFoundException {
- clickOn(new UiSelector().resourceId("com.android.printspooler:id/destination_spinner"));
-
- clickOnText(printerName);
- }
-
- /**
- * Init mock print servic that returns a single printer by default.
- *
- * @param sessionRef Where to store the reference to the session once started
- */
- private void setMockPrintServiceCallbacks(StubbablePrinterDiscoverySession[] sessionRef) {
- MockPrintService.setCallbacks(createMockPrintServiceCallbacks(
- inv -> createMockPrinterDiscoverySessionCallbacks(inv2 -> {
- synchronized (sessionRef) {
- sessionRef[0] = ((PrinterDiscoverySessionCallbacks) inv2.getMock())
- .getSession();
-
- addPrinter(sessionRef[0], "1st printer",
- PrintAttributes.MediaSize.ISO_A0);
-
- sessionRef.notifyAll();
- }
- return null;
- },
- null, null, null, null, null, inv2 -> {
- synchronized (sessionRef) {
- sessionRef[0] = null;
- sessionRef.notifyAll();
- }
- return null;
- }
- ), null, null));
- }
-
- /**
- * Start print operation that just prints a single empty page
- *
- * @param printAttributesRef Where to store the reference to the print attributes once started
- */
- private void print(PrintAttributes[] printAttributesRef) {
- print(new PrintDocumentAdapter() {
- @Override
- public void onStart() {
- }
-
- @Override
- public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
- CancellationSignal cancellationSignal, LayoutResultCallback callback,
- Bundle extras) {
- callback.onLayoutFinished((new PrintDocumentInfo.Builder("doc")).build(),
- !newAttributes.equals(printAttributesRef[0]));
-
- synchronized (printAttributesRef) {
- printAttributesRef[0] = newAttributes;
- printAttributesRef.notifyAll();
- }
- }
-
- @Override
- public void onWrite(PageRange[] pages, ParcelFileDescriptor destination,
- CancellationSignal cancellationSignal, WriteResultCallback callback) {
- try {
- try {
- PrintedPdfDocument document = new PrintedPdfDocument(getActivity(),
- printAttributesRef[0]);
- try {
- PdfDocument.Page page = document.startPage(0);
- document.finishPage(page);
- try (FileOutputStream os = new FileOutputStream(
- destination.getFileDescriptor())) {
- document.writeTo(os);
- os.flush();
- }
- } finally {
- document.close();
- }
- } finally {
- destination.close();
- }
-
- callback.onWriteFinished(pages);
- } catch (IOException e) {
- callback.onWriteFailed(e.getMessage());
- }
- }
- }, null);
- }
-
- @Test
- @LargeTest
- public void addAndSelectPrinter() throws Exception {
- final StubbablePrinterDiscoverySession session[] = new StubbablePrinterDiscoverySession[1];
- final PrintAttributes printAttributes[] = new PrintAttributes[1];
-
- setMockPrintServiceCallbacks(session);
- print(printAttributes);
-
- // We are now in the PrintActivity
- Log.i(LOG_TAG, "Waiting for session");
- synchronized (session) {
- waitWithTimeout(() -> session[0] == null, session::wait);
- }
-
- setPrinter("1st printer");
-
- Log.i(LOG_TAG, "Waiting for print attributes to change");
- synchronized (printAttributes) {
- waitWithTimeout(
- () -> printAttributes[0] == null || !printAttributes[0].getMediaSize().equals(
- PrintAttributes.MediaSize.ISO_A0), printAttributes::wait);
- }
-
- setPrinter("All printers\u2026");
-
- // We are now in the SelectPrinterActivity
- clickOnText("Add printer");
-
- // We are now in the AddPrinterActivity
- AddPrintersActivity.addObserver(
- () -> addPrinter(session[0], "2nd printer", PrintAttributes.MediaSize.ISO_A1));
-
- // This executes the observer registered above
- clickOn(new UiSelector().text(MockPrintService.class.getCanonicalName())
- .resourceId("com.android.printspooler:id/title"));
-
- getUiDevice().pressBack();
- AddPrintersActivity.clearObservers();
-
- // We are now in the SelectPrinterActivity
- clickOnText("2nd printer");
-
- // We are now in the PrintActivity
- Log.i(LOG_TAG, "Waiting for print attributes to change");
- synchronized (printAttributes) {
- waitWithTimeout(
- () -> printAttributes[0] == null || !printAttributes[0].getMediaSize().equals(
- PrintAttributes.MediaSize.ISO_A1), printAttributes::wait);
- }
-
- getUiDevice().pressBack();
-
- // We are back in the test activity
- Log.i(LOG_TAG, "Waiting for session to end");
- synchronized (session) {
- waitWithTimeout(() -> session[0] != null, session::wait);
- }
- }
-
- @Test
- @LargeTest
- public void abortSelectingPrinter() throws Exception {
- final StubbablePrinterDiscoverySession session[] = new StubbablePrinterDiscoverySession[1];
- final PrintAttributes printAttributes[] = new PrintAttributes[1];
-
- setMockPrintServiceCallbacks(session);
- print(printAttributes);
-
- // We are now in the PrintActivity
- Log.i(LOG_TAG, "Waiting for session");
- synchronized (session) {
- waitWithTimeout(() -> session[0] == null, session::wait);
- }
-
- setPrinter("1st printer");
-
- Log.i(LOG_TAG, "Waiting for print attributes to change");
- synchronized (printAttributes) {
- waitWithTimeout(
- () -> printAttributes[0] == null || !printAttributes[0].getMediaSize().equals(
- PrintAttributes.MediaSize.ISO_A0), printAttributes::wait);
- }
-
- setPrinter("All printers\u2026");
-
- // We are now in the SelectPrinterActivity
- clickOnText("Add printer");
-
- // We are now in the AddPrinterActivity
- AddPrintersActivity.addObserver(
- () -> addPrinter(session[0], "2nd printer", PrintAttributes.MediaSize.ISO_A1));
-
- // This executes the observer registered above
- clickOn(new UiSelector().text(MockPrintService.class.getCanonicalName())
- .resourceId("com.android.printspooler:id/title"));
-
- getUiDevice().pressBack();
- AddPrintersActivity.clearObservers();
-
- // Do not select a new printer, just press back
- getUiDevice().pressBack();
-
- // We are now in the PrintActivity
- // The media size should not change
- Log.i(LOG_TAG, "Make sure print attributes did not change");
- Thread.sleep(100);
- assertEquals(PrintAttributes.MediaSize.ISO_A0, printAttributes[0].getMediaSize());
-
- getUiDevice().pressBack();
-
- // We are back in the test activity
- Log.i(LOG_TAG, "Waiting for session to end");
- synchronized (session) {
- waitWithTimeout(() -> session[0] != null, session::wait);
- }
- }
-}
diff --git a/core/tests/systemproperties/Android.mk b/core/tests/systemproperties/Android.mk
index ffc1282..e16c367 100644
--- a/core/tests/systemproperties/Android.mk
+++ b/core/tests/systemproperties/Android.mk
@@ -11,7 +11,6 @@
LOCAL_STATIC_JAVA_LIBRARIES := android-common frameworks-core-util-lib
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := FrameworksCoreSystemPropertiesTests
-LOCAL_JAVA_LANGUAGE_VERSION := 1.8
LOCAL_CERTIFICATE := platform
diff --git a/docs/html/_redirects.yaml b/docs/html/_redirects.yaml
index 8cb2ee7..7200d47 100644
--- a/docs/html/_redirects.yaml
+++ b/docs/html/_redirects.yaml
@@ -806,7 +806,7 @@
- from: /preview/features/power-mgmt.html
to: /training/monitoring-device-state/doze-standby.html
- from: /preview/dev-community
- to: https://plus.google.com/communities/103655397235276743411
+ to: https://plus.google.com/communities/105153134372062985968
- from: /preview/bug
to: https://source.android.com/source/report-bugs.html
- from: /preview/bug/...
@@ -1290,6 +1290,8 @@
to: /studio/write/draw9patch.html?utm_source=android-studio
- from: /r/studio-ui/firebase-assistant.html
to: /studio/write/firebase.html?utm_source=android-studio
+- from: /r/studio-ui/ir-flight-recorder.html
+ to: /studio/run/index.html?utm_source=android-studio#submit-feedback
# Redirects from (removed) N Preview documentation
- from: /preview/features/afw.html
diff --git a/docs/html/guide/topics/ui/drag-drop.jd b/docs/html/guide/topics/ui/drag-drop.jd
index d6e07a56..9280818 100644
--- a/docs/html/guide/topics/ui/drag-drop.jd
+++ b/docs/html/guide/topics/ui/drag-drop.jd
@@ -157,19 +157,22 @@
</p>
<p>
Your application tells the system to start a drag by calling the
- {@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()}
+ {@link android.view.View#startDragAndDrop(ClipData,View.DragShadowBuilder,Object,int)
+ startDragAndDrop()}
method. This tells the system to start sending drag events. The method also sends the data that
you are dragging.
</p>
<p>
You can call
- {@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()}
+ {@link android.view.View#startDragAndDrop(ClipData,View.DragShadowBuilder,Object,int)
+ startDragAndDrop()}
for any attached View in the current layout. The system only uses the View object to get access
to global settings in your layout.
</p>
<p>
Once your application calls
- {@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()},
+ {@link android.view.View#startDragAndDrop(ClipData,View.DragShadowBuilder,Object,int)
+ startDragAndDrop()},
the rest of the process uses events that the system sends to the View objects in your current
layout.
</p>
@@ -183,11 +186,13 @@
</dt>
<dd>
In response to the user's gesture to begin a drag, your application calls
- {@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()}
- to tell the system to start a drag. The arguments
- {@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()}
- provide the data to be dragged, metadata for this data, and a callback for drawing the
- drag shadow.
+ {@link android.view.View#startDragAndDrop(ClipData,View.DragShadowBuilder,Object,int)
+ startDragAndDrop()}
+ to tell the system to start a drag. The
+ {@link android.view.View#startDragAndDrop(ClipData,View.DragShadowBuilder,Object,int)
+ startDragAndDrop()}
+ arguments provide the data to be dragged, metadata for this data, and a callback for drawing
+ the drag shadow.
<p>
The system first responds by calling back to your application to get a drag shadow. It
then displays the drag shadow on the device.
@@ -199,12 +204,13 @@
including a possible drop event, a drag event listener must return <code>true</code>.
This registers the listener with the system. Only registered listeners continue to
receive drag events. At this point, listeners can also change the appearance of their
- View object to show that the listener can accept a drop event.
+ View object to show that the listener can accept the dragged data.
</p>
<p>
If the drag event listener returns <code>false</code>, then it will not receive drag
- events for the current operation until the system sends a drag event with action type
- {@link android.view.DragEvent#ACTION_DRAG_ENDED}. By sending <code>false</code>, the
+ events for the current operation, including the drag event with action type
+ {@link android.view.DragEvent#ACTION_DRAG_ENDED} that will conclude the
+ operation. By sending <code>false</code>, the
listener tells the system that it is not interested in the drag operation and
does not want to accept the dragged data.
</p>
@@ -230,7 +236,8 @@
object's listener a drag event with action type
{@link android.view.DragEvent#ACTION_DROP}. The drag event contains the data that was
passed to the system in the call to
- {@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()}
+ {@link android.view.View#startDragAndDrop(ClipData,View.DragShadowBuilder,Object,int)
+ startDragAndDrop()}
that started the operation. The listener is expected to return boolean <code>true</code> to
the system if code for accepting the drop succeeds.
<p>
@@ -297,7 +304,8 @@
<p>
The {@link android.view.DragEvent} object also contains the data that your application provided
to the system in the call to
- {@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()}.
+ {@link android.view.View#startDragAndDrop(ClipData,View.DragShadowBuilder,Object,int)
+ startDragAndDrop()}.
Some of the data is valid only for certain action types. The data that is valid for each action
type is summarized in <a href="#table2">table 2</a>. It is also described in detail with
the event for which it is valid in the section
@@ -316,8 +324,9 @@
<td>
A View object's drag event listener receives this event action type just after the
application calls
-{@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()} and
- gets a drag shadow.
+ {@link android.view.View#startDragAndDrop(ClipData,View.DragShadowBuilder,Object,int)
+ startDragAndDrop()}
+ and gets a drag shadow.
<p>
If the listener wants to continue receiving drag events for this operation, it must
return boolean <code>true</code> to the system.
@@ -345,8 +354,7 @@
<td>{@link android.view.DragEvent#ACTION_DRAG_EXITED}</td>
<td>
A View object's drag event listener receives this event action type after it receives a
- {@link android.view.DragEvent#ACTION_DRAG_ENTERED} and at least one
- {@link android.view.DragEvent#ACTION_DRAG_LOCATION} event, and after the user has moved
+ {@link android.view.DragEvent#ACTION_DRAG_ENTERED} event, and after the user has moved
the drag shadow outside the bounding box of the View or into a descendant view that can
accept the data.
</td>
@@ -355,7 +363,8 @@
<td>{@link android.view.DragEvent#ACTION_DROP}</td>
<td>
A View object's drag event listener receives this event action type when the user
- releases the drag shadow over the View object. This action type is only sent to a View
+ releases the drag shadow over the View object and not over its descendant view that can
+ accept the drag data. This action type is only sent to a View
object's listener if the listener returned boolean <code>true</code> in response to the
{@link android.view.DragEvent#ACTION_DRAG_STARTED} drag event. This action type is not
sent if the user releases the drag shadow on a View whose listener is not registered,
@@ -472,9 +481,11 @@
The image is called a drag shadow. You create it with methods you declare for a
{@link android.view.View.DragShadowBuilder} object, and then pass it to the system when you
start a drag using
- {@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()}.
+ {@link android.view.View#startDragAndDrop(ClipData,View.DragShadowBuilder,Object,int)
+ startDragAndDrop()}.
As part of its response to
- {@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()},
+ {@link android.view.View#startDragAndDrop(ClipData,View.DragShadowBuilder,Object,int)
+ startDragAndDrop()},
the system invokes the callback methods you've defined in
{@link android.view.View.DragShadowBuilder} to obtain a drag shadow.
</p>
@@ -516,7 +527,8 @@
</dt>
<dd>
The system calls this method immediately after you call
-{@link android.view.View#startDrag(ClipData,View.DragShadowBuilder,Object,int) startDrag()}. Use it
+ {@link android.view.View#startDragAndDrop(ClipData,View.DragShadowBuilder,Object,int)
+ startDragAndDrop()}. Use it
to send to the system the dimensions and touch point of the drag shadow. The method has two
arguments:
<dl>
@@ -616,10 +628,10 @@
// Starts the drag
- v.startDrag(dragData, // the data to be dragged
- myShadow, // the drag shadow builder
- null, // no need to use local data
- 0 // flags (not currently used, set to 0)
+ v.startDragAndDrop(dragData, // the data to be dragged
+ myShadow, // the drag shadow builder
+ null, // no need to use local data
+ 0 // flags (not currently used, set to 0)
);
}
@@ -722,8 +734,7 @@
<p>
Note that for an {@link android.view.DragEvent#ACTION_DRAG_STARTED} event, these
the following {@link android.view.DragEvent} methods are not valid:
- {@link android.view.DragEvent#getClipData()}, {@link android.view.DragEvent#getX()},
- {@link android.view.DragEvent#getY()}, and {@link android.view.DragEvent#getResult()}.
+ {@link android.view.DragEvent#getClipData()} and {@link android.view.DragEvent#getResult()}.
</p>
<h3 id="HandleDuring">Handling events during the drag</h3>
<p>
@@ -751,7 +762,9 @@
{@link android.view.DragEvent#ACTION_DRAG_LOCATION}: Once the listener receives an
{@link android.view.DragEvent#ACTION_DRAG_ENTERED} event, and before it receives an
A{@link android.view.DragEvent#ACTION_DRAG_EXITED} event, it receives a new
- {@link android.view.DragEvent#ACTION_DRAG_LOCATION} event every time the touch point moves.
+ {@link android.view.DragEvent#ACTION_DRAG_LOCATION} event immediately after the
+ {@link android.view.DragEvent#ACTION_DRAG_ENTERED} event, and then every time the touch
+ point moves.
The {@link android.view.DragEvent#getX()} and {@link android.view.DragEvent#getY()} methods
return the X and Y coordinates of the touch point.
</li>
@@ -769,9 +782,9 @@
</p>
<ul>
<li>
- In response to {@link android.view.DragEvent#ACTION_DRAG_ENTERED} or
- {@link android.view.DragEvent#ACTION_DRAG_LOCATION}, the listener can change the appearance
- of the View to indicate that it is about to receive a drop.
+ In response to {@link android.view.DragEvent#ACTION_DRAG_ENTERED}, the listener can change
+ the appearance
+ of the View to indicate that it is ready to receive a drop.
</li>
<li>
An event with the action type {@link android.view.DragEvent#ACTION_DRAG_LOCATION} contains
@@ -784,14 +797,14 @@
<li>
In response to {@link android.view.DragEvent#ACTION_DRAG_EXITED}, the listener should reset
any appearance changes it applied in response to
- {@link android.view.DragEvent#ACTION_DRAG_ENTERED} or
- {@link android.view.DragEvent#ACTION_DRAG_LOCATION}. This indicates to the user that
+ {@link android.view.DragEvent#ACTION_DRAG_ENTERED}. This indicates to the user that
the View is no longer an imminent drop target.
</li>
</ul>
<h3 id="HandleDrop">Responding to a drop</h3>
<p>
- When the user releases the drag shadow on a View in the application, and that View previously
+ When the user releases the drag shadow on a View in the application, but not on its descendant
+ view that can accept the data, and that View previously
reported that it could accept the content being dragged, the system dispatches a drag event
to that View with the action type {@link android.view.DragEvent#ACTION_DROP}. The listener
should do the following:
@@ -800,8 +813,8 @@
<li>
Call {@link android.view.DragEvent#getClipData()} to get the
{@link android.content.ClipData} object that was originally supplied in the call
- to
-{@link android.view.View#startDrag(ClipData, View.DragShadowBuilder, Object, int) startDrag()}
+ to {@link android.view.View#startDragAndDrop(ClipData, View.DragShadowBuilder, Object, int)
+ startDragAndDrop()}
and store it. If the drag and drop operation does not represent data movement,
this may not be necessary.
</li>
@@ -856,9 +869,6 @@
including any case in which the system did not send out a
{@link android.view.DragEvent#ACTION_DROP} event.
</li>
- <li>
- The listener should return boolean <code>true</code> to the system.
- </li>
</ol>
<p>
</p>
diff --git a/docs/html/jd_extras_zh-cn.js b/docs/html/jd_extras_zh-cn.js
index cb1ccb7..866a87e 100644
--- a/docs/html/jd_extras_zh-cn.js
+++ b/docs/html/jd_extras_zh-cn.js
@@ -244,40 +244,40 @@
"overview/zhcn/1": {
"title": "",
"resources": [
- "intl/zh-cn/distribute/essentials/quality/core.html",
- "intl/zh-cn/distribute/essentials/quality/tablets.html",
- "intl/zh-cn/distribute/tools/launch-checklist.html",
- "intl/zh-cn/tools/publishing/publishing_overview.html",
- "intl/zh-cn/distribute/tools/localization-checklist.html"
+ "distribute/essentials/quality/core.html",
+ "distribute/essentials/quality/tablets.html",
+ "distribute/tools/launch-checklist.html",
+ "tools/publishing/publishing_overview.html",
+ "distribute/tools/localization-checklist.html"
]
},
"overview/zhcn/2": {
"title": "",
"resources": [
- "intl/zh-cn/google/play/billing/index.html",
- "intl/zh-cn/google/play/billing/api.html",
- "intl/zh-cn/google/play/billing/billing_admin.html",
- "intl/zh-cn/google/play/billing/billing_testing.html",
- "intl/zh-cn/google/play/billing/billing_best_practices.html"
+ "google/play/billing/index.html",
+ "google/play/billing/api.html",
+ "google/play/billing/billing_admin.html",
+ "google/play/billing/billing_testing.html",
+ "google/play/billing/billing_best_practices.html"
]
},
"overview/zhcn/3": {
"title": "",
"resources": [
"https://play.google.com/intl/en_us/badges/",
- "intl/zh-cn/distribute/tools/promote/device-art.html",
- "intl/zh-cn/distribute/tools/promote/linking.html",
- "intl/zh-cn/distribute/tools/promote/brand.html",
- "intl/zh-cn/tools/help/proguard.html"
+ "distribute/tools/promote/device-art.html",
+ "distribute/tools/promote/linking.html",
+ "distribute/tools/promote/brand.html",
+ "tools/help/proguard.html"
]
},
"overview/zhcn/4": {
"title": "",
"resources": [
- "intl/zh-cn/design/style/writing.html",
- "intl/zh-cn/training/basics/fragments/fragment-ui.html",
- "intl/zh-cn/training/multiscreen/index.html",
- "intl/zh-cn/training/monitoring-device-state/index.html"
+ "design/style/writing.html",
+ "training/basics/fragments/fragment-ui.html",
+ "training/multiscreen/index.html",
+ "training/monitoring-device-state/index.html"
]
},
"overview/carousel/zhcn": {
diff --git a/docs/html/wear/preview/downloads.jd b/docs/html/wear/preview/downloads.jd
index 83a3f98..da6a06d 100644
--- a/docs/html/wear/preview/downloads.jd
+++ b/docs/html/wear/preview/downloads.jd
@@ -653,6 +653,12 @@
"{@docRoot}studio/intro/update.html#sdk-manager">SDK Manager</a>.
</p>
+ <p>
+ After you create a virtual device as described below, follow the steps for
+ <a href="#set_up_a_phone">setting up a phone</a> with the beta version of
+ the Android Wear companion app.
+ </p>
+
<p>Create a new virtual device in Android Studio as follows:
</p>
@@ -696,7 +702,9 @@
</p>
<ol>
- <li>On the phone, install the Android Wear app from Google Play.
+ <li>Follow the steps for
+ <a href="#set_up_a_phone">setting up a phone</a> with the beta version of
+ the Android Wear companion app.
</li>
<li>On the phone, enable Developer Options and USB Debugging.
@@ -740,7 +748,7 @@
in the <a href=
"{@docRoot}tools/devices/emulator.html">Android Emulator</a>. For more
information about using virtual devices, see <a href=
- "{@docRoot}tools/devices/managing-avds.html">
+ "{@docRoot}studio/run/managing-avds.html">
Create and Manage Virtual Devices</a>.
</p>
diff --git a/docs/html/wear/preview/features/app-distribution.jd b/docs/html/wear/preview/features/app-distribution.jd
index 319efa6..afc9516 100644
--- a/docs/html/wear/preview/features/app-distribution.jd
+++ b/docs/html/wear/preview/features/app-distribution.jd
@@ -132,6 +132,9 @@
<pre>
android {
+ // Allows you to reference product flavors in your
+ // phone module's build.gradle file
+ publishNonDefault true
...
defaultConfig
{
@@ -148,6 +151,7 @@
minSdkVersion 24
}
}
+}
</pre>
<p>
@@ -158,7 +162,7 @@
<pre>
dependencies {
...
- wearApp project(path: ':wearable', configuration: 'wear1Release')
+ wearApp project(path: ':wear', configuration: 'wear1Release')
}
</pre>
diff --git a/graphics/java/android/graphics/Matrix.java b/graphics/java/android/graphics/Matrix.java
index 1e8f11b..35cedae 100644
--- a/graphics/java/android/graphics/Matrix.java
+++ b/graphics/java/android/graphics/Matrix.java
@@ -16,8 +16,12 @@
package android.graphics;
-import java.io.PrintWriter;
+import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
+import libcore.util.NativeAllocationRegistry;
+
+import java.io.PrintWriter;
/**
* The Matrix class holds a 3x3 matrix for transforming coordinates.
@@ -216,352 +220,345 @@
}
};
+ // sizeof(SkMatrix) is 9 * sizeof(float) + uint32_t
+ private static final long NATIVE_ALLOCATION_SIZE = 40;
+
+ private static class NoImagePreloadHolder {
+ public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+ Matrix.class.getClassLoader(), nGetNativeFinalizer(), NATIVE_ALLOCATION_SIZE);
+ }
+
/**
* @hide
*/
- public long native_instance;
+ public final long native_instance;
/**
* Create an identity matrix
*/
public Matrix() {
- native_instance = native_create(0);
+ native_instance = nCreate(0);
+ NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, native_instance);
}
/**
* Create a matrix that is a (deep) copy of src
+ *
* @param src The matrix to copy into this matrix
*/
public Matrix(Matrix src) {
- native_instance = native_create(src != null ? src.native_instance : 0);
+ native_instance = nCreate(src != null ? src.native_instance : 0);
+ NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, native_instance);
}
/**
- * Returns true if the matrix is identity.
- * This maybe faster than testing if (getType() == 0)
+ * Returns true if the matrix is identity. This maybe faster than testing if (getType() == 0)
*/
public boolean isIdentity() {
- return native_isIdentity(native_instance);
+ return nIsIdentity(native_instance);
}
/**
- * Gets whether this matrix is affine. An affine matrix preserves
- * straight lines and has no perspective.
+ * Gets whether this matrix is affine. An affine matrix preserves straight lines and has no
+ * perspective.
*
* @return Whether the matrix is affine.
*/
public boolean isAffine() {
- return native_isAffine(native_instance);
+ return nIsAffine(native_instance);
}
/**
- * Returns true if will map a rectangle to another rectangle. This can be
- * true if the matrix is identity, scale-only, or rotates a multiple of 90
- * degrees.
+ * Returns true if will map a rectangle to another rectangle. This can be true if the matrix is
+ * identity, scale-only, or rotates a multiple of 90 degrees.
*/
public boolean rectStaysRect() {
- return native_rectStaysRect(native_instance);
+ return nRectStaysRect(native_instance);
}
/**
- * (deep) copy the src matrix into this matrix. If src is null, reset this
- * matrix to the identity matrix.
+ * (deep) copy the src matrix into this matrix. If src is null, reset this matrix to the
+ * identity matrix.
*/
public void set(Matrix src) {
if (src == null) {
reset();
} else {
- native_set(native_instance, src.native_instance);
+ nSet(native_instance, src.native_instance);
}
}
- /** Returns true iff obj is a Matrix and its values equal our values.
- */
+ /**
+ * Returns true iff obj is a Matrix and its values equal our values.
+ */
@Override
public boolean equals(Object obj) {
- //if (obj == this) return true; -- NaN value would mean matrix != itself
- if (!(obj instanceof Matrix)) return false;
- return native_equals(native_instance, ((Matrix)obj).native_instance);
+ // if (obj == this) return true; -- NaN value would mean matrix != itself
+ if (!(obj instanceof Matrix)) {
+ return false;
+ }
+ return nEquals(native_instance, ((Matrix) obj).native_instance);
}
@Override
public int hashCode() {
// This should generate the hash code by performing some arithmetic operation on all
// the matrix elements -- our equals() does an element-by-element comparison, and we
- // need to ensure that the hash code for two equal objects is the same. We're not
+ // need to ensure that the hash code for two equal objects is the same. We're not
// really using this at the moment, so we take the easy way out.
return 44;
}
/** Set the matrix to identity */
public void reset() {
- native_reset(native_instance);
+ nReset(native_instance);
}
/** Set the matrix to translate by (dx, dy). */
public void setTranslate(float dx, float dy) {
- native_setTranslate(native_instance, dx, dy);
+ nSetTranslate(native_instance, dx, dy);
}
/**
- * Set the matrix to scale by sx and sy, with a pivot point at (px, py).
- * The pivot point is the coordinate that should remain unchanged by the
- * specified transformation.
+ * Set the matrix to scale by sx and sy, with a pivot point at (px, py). The pivot point is the
+ * coordinate that should remain unchanged by the specified transformation.
*/
public void setScale(float sx, float sy, float px, float py) {
- native_setScale(native_instance, sx, sy, px, py);
+ nSetScale(native_instance, sx, sy, px, py);
}
/** Set the matrix to scale by sx and sy. */
public void setScale(float sx, float sy) {
- native_setScale(native_instance, sx, sy);
+ nSetScale(native_instance, sx, sy);
}
/**
- * Set the matrix to rotate by the specified number of degrees, with a pivot
- * point at (px, py). The pivot point is the coordinate that should remain
- * unchanged by the specified transformation.
+ * Set the matrix to rotate by the specified number of degrees, with a pivot point at (px, py).
+ * The pivot point is the coordinate that should remain unchanged by the specified
+ * transformation.
*/
public void setRotate(float degrees, float px, float py) {
- native_setRotate(native_instance, degrees, px, py);
+ nSetRotate(native_instance, degrees, px, py);
}
/**
* Set the matrix to rotate about (0,0) by the specified number of degrees.
*/
public void setRotate(float degrees) {
- native_setRotate(native_instance, degrees);
+ nSetRotate(native_instance, degrees);
}
/**
- * Set the matrix to rotate by the specified sine and cosine values, with a
- * pivot point at (px, py). The pivot point is the coordinate that should
- * remain unchanged by the specified transformation.
+ * Set the matrix to rotate by the specified sine and cosine values, with a pivot point at (px,
+ * py). The pivot point is the coordinate that should remain unchanged by the specified
+ * transformation.
*/
public void setSinCos(float sinValue, float cosValue, float px, float py) {
- native_setSinCos(native_instance, sinValue, cosValue, px, py);
+ nSetSinCos(native_instance, sinValue, cosValue, px, py);
}
/** Set the matrix to rotate by the specified sine and cosine values. */
public void setSinCos(float sinValue, float cosValue) {
- native_setSinCos(native_instance, sinValue, cosValue);
+ nSetSinCos(native_instance, sinValue, cosValue);
}
/**
- * Set the matrix to skew by sx and sy, with a pivot point at (px, py).
- * The pivot point is the coordinate that should remain unchanged by the
- * specified transformation.
+ * Set the matrix to skew by sx and sy, with a pivot point at (px, py). The pivot point is the
+ * coordinate that should remain unchanged by the specified transformation.
*/
public void setSkew(float kx, float ky, float px, float py) {
- native_setSkew(native_instance, kx, ky, px, py);
+ nSetSkew(native_instance, kx, ky, px, py);
}
/** Set the matrix to skew by sx and sy. */
public void setSkew(float kx, float ky) {
- native_setSkew(native_instance, kx, ky);
+ nSetSkew(native_instance, kx, ky);
}
/**
- * Set the matrix to the concatenation of the two specified matrices and
- * return true.
- *
- * <p>Either of the two matrices may also be the target matrix, that is
- * <code>matrixA.setConcat(matrixA, matrixB);</code> is valid.</p>
- *
- * <p class="note">In {@link android.os.Build.VERSION_CODES#GINGERBREAD_MR1} and below, this
- * function returns true only if the result can be represented. In
- * {@link android.os.Build.VERSION_CODES#HONEYCOMB} and above, it always returns true.</p>
+ * Set the matrix to the concatenation of the two specified matrices and return true.
+ * <p>
+ * Either of the two matrices may also be the target matrix, that is
+ * <code>matrixA.setConcat(matrixA, matrixB);</code> is valid.
+ * </p>
+ * <p class="note">
+ * In {@link android.os.Build.VERSION_CODES#GINGERBREAD_MR1} and below, this function returns
+ * true only if the result can be represented. In
+ * {@link android.os.Build.VERSION_CODES#HONEYCOMB} and above, it always returns true.
+ * </p>
*/
public boolean setConcat(Matrix a, Matrix b) {
- native_setConcat(native_instance, a.native_instance, b.native_instance);
+ nSetConcat(native_instance, a.native_instance, b.native_instance);
return true;
}
/**
- * Preconcats the matrix with the specified translation.
- * M' = M * T(dx, dy)
+ * Preconcats the matrix with the specified translation. M' = M * T(dx, dy)
*/
public boolean preTranslate(float dx, float dy) {
- native_preTranslate(native_instance, dx, dy);
+ nPreTranslate(native_instance, dx, dy);
return true;
}
/**
- * Preconcats the matrix with the specified scale.
- * M' = M * S(sx, sy, px, py)
+ * Preconcats the matrix with the specified scale. M' = M * S(sx, sy, px, py)
*/
public boolean preScale(float sx, float sy, float px, float py) {
- native_preScale(native_instance, sx, sy, px, py);
+ nPreScale(native_instance, sx, sy, px, py);
return true;
}
/**
- * Preconcats the matrix with the specified scale.
- * M' = M * S(sx, sy)
+ * Preconcats the matrix with the specified scale. M' = M * S(sx, sy)
*/
public boolean preScale(float sx, float sy) {
- native_preScale(native_instance, sx, sy);
+ nPreScale(native_instance, sx, sy);
return true;
}
/**
- * Preconcats the matrix with the specified rotation.
- * M' = M * R(degrees, px, py)
+ * Preconcats the matrix with the specified rotation. M' = M * R(degrees, px, py)
*/
public boolean preRotate(float degrees, float px, float py) {
- native_preRotate(native_instance, degrees, px, py);
+ nPreRotate(native_instance, degrees, px, py);
return true;
}
/**
- * Preconcats the matrix with the specified rotation.
- * M' = M * R(degrees)
+ * Preconcats the matrix with the specified rotation. M' = M * R(degrees)
*/
public boolean preRotate(float degrees) {
- native_preRotate(native_instance, degrees);
+ nPreRotate(native_instance, degrees);
return true;
}
/**
- * Preconcats the matrix with the specified skew.
- * M' = M * K(kx, ky, px, py)
+ * Preconcats the matrix with the specified skew. M' = M * K(kx, ky, px, py)
*/
public boolean preSkew(float kx, float ky, float px, float py) {
- native_preSkew(native_instance, kx, ky, px, py);
+ nPreSkew(native_instance, kx, ky, px, py);
return true;
}
/**
- * Preconcats the matrix with the specified skew.
- * M' = M * K(kx, ky)
+ * Preconcats the matrix with the specified skew. M' = M * K(kx, ky)
*/
public boolean preSkew(float kx, float ky) {
- native_preSkew(native_instance, kx, ky);
+ nPreSkew(native_instance, kx, ky);
return true;
}
/**
- * Preconcats the matrix with the specified matrix.
- * M' = M * other
+ * Preconcats the matrix with the specified matrix. M' = M * other
*/
public boolean preConcat(Matrix other) {
- native_preConcat(native_instance, other.native_instance);
+ nPreConcat(native_instance, other.native_instance);
return true;
}
/**
- * Postconcats the matrix with the specified translation.
- * M' = T(dx, dy) * M
+ * Postconcats the matrix with the specified translation. M' = T(dx, dy) * M
*/
public boolean postTranslate(float dx, float dy) {
- native_postTranslate(native_instance, dx, dy);
+ nPostTranslate(native_instance, dx, dy);
return true;
}
/**
- * Postconcats the matrix with the specified scale.
- * M' = S(sx, sy, px, py) * M
+ * Postconcats the matrix with the specified scale. M' = S(sx, sy, px, py) * M
*/
public boolean postScale(float sx, float sy, float px, float py) {
- native_postScale(native_instance, sx, sy, px, py);
+ nPostScale(native_instance, sx, sy, px, py);
return true;
}
/**
- * Postconcats the matrix with the specified scale.
- * M' = S(sx, sy) * M
+ * Postconcats the matrix with the specified scale. M' = S(sx, sy) * M
*/
public boolean postScale(float sx, float sy) {
- native_postScale(native_instance, sx, sy);
+ nPostScale(native_instance, sx, sy);
return true;
}
/**
- * Postconcats the matrix with the specified rotation.
- * M' = R(degrees, px, py) * M
+ * Postconcats the matrix with the specified rotation. M' = R(degrees, px, py) * M
*/
public boolean postRotate(float degrees, float px, float py) {
- native_postRotate(native_instance, degrees, px, py);
+ nPostRotate(native_instance, degrees, px, py);
return true;
}
/**
- * Postconcats the matrix with the specified rotation.
- * M' = R(degrees) * M
+ * Postconcats the matrix with the specified rotation. M' = R(degrees) * M
*/
public boolean postRotate(float degrees) {
- native_postRotate(native_instance, degrees);
+ nPostRotate(native_instance, degrees);
return true;
}
/**
- * Postconcats the matrix with the specified skew.
- * M' = K(kx, ky, px, py) * M
+ * Postconcats the matrix with the specified skew. M' = K(kx, ky, px, py) * M
*/
public boolean postSkew(float kx, float ky, float px, float py) {
- native_postSkew(native_instance, kx, ky, px, py);
+ nPostSkew(native_instance, kx, ky, px, py);
return true;
}
/**
- * Postconcats the matrix with the specified skew.
- * M' = K(kx, ky) * M
+ * Postconcats the matrix with the specified skew. M' = K(kx, ky) * M
*/
public boolean postSkew(float kx, float ky) {
- native_postSkew(native_instance, kx, ky);
+ nPostSkew(native_instance, kx, ky);
return true;
}
/**
- * Postconcats the matrix with the specified matrix.
- * M' = other * M
+ * Postconcats the matrix with the specified matrix. M' = other * M
*/
public boolean postConcat(Matrix other) {
- native_postConcat(native_instance, other.native_instance);
+ nPostConcat(native_instance, other.native_instance);
return true;
}
- /** Controlls how the src rect should align into the dst rect for
- setRectToRect().
- */
+ /**
+ * Controlls how the src rect should align into the dst rect for setRectToRect().
+ */
public enum ScaleToFit {
/**
- * Scale in X and Y independently, so that src matches dst exactly.
- * This may change the aspect ratio of the src.
+ * Scale in X and Y independently, so that src matches dst exactly. This may change the
+ * aspect ratio of the src.
*/
- FILL (0),
+ FILL(0),
/**
- * Compute a scale that will maintain the original src aspect ratio,
- * but will also ensure that src fits entirely inside dst. At least one
- * axis (X or Y) will fit exactly. START aligns the result to the
- * left and top edges of dst.
+ * Compute a scale that will maintain the original src aspect ratio, but will also ensure
+ * that src fits entirely inside dst. At least one axis (X or Y) will fit exactly. START
+ * aligns the result to the left and top edges of dst.
*/
- START (1),
+ START(1),
/**
- * Compute a scale that will maintain the original src aspect ratio,
- * but will also ensure that src fits entirely inside dst. At least one
- * axis (X or Y) will fit exactly. The result is centered inside dst.
+ * Compute a scale that will maintain the original src aspect ratio, but will also ensure
+ * that src fits entirely inside dst. At least one axis (X or Y) will fit exactly. The
+ * result is centered inside dst.
*/
- CENTER (2),
+ CENTER(2),
/**
- * Compute a scale that will maintain the original src aspect ratio,
- * but will also ensure that src fits entirely inside dst. At least one
- * axis (X or Y) will fit exactly. END aligns the result to the
- * right and bottom edges of dst.
+ * Compute a scale that will maintain the original src aspect ratio, but will also ensure
+ * that src fits entirely inside dst. At least one axis (X or Y) will fit exactly. END
+ * aligns the result to the right and bottom edges of dst.
*/
- END (3);
+ END(3);
// the native values must match those in SkMatrix.h
ScaleToFit(int nativeInt) {
this.nativeInt = nativeInt;
}
+
final int nativeInt;
}
/**
- * Set the matrix to the scale and translate values that map the source
- * rectangle to the destination rectangle, returning true if the the result
- * can be represented.
+ * Set the matrix to the scale and translate values that map the source rectangle to the
+ * destination rectangle, returning true if the the result can be represented.
*
* @param src the source rectangle to map from.
* @param dst the destination rectangle to map to.
@@ -572,13 +569,13 @@
if (dst == null || src == null) {
throw new NullPointerException();
}
- return native_setRectToRect(native_instance, src, dst, stf.nativeInt);
+ return nSetRectToRect(native_instance, src, dst, stf.nativeInt);
}
// private helper to perform range checks on arrays of "points"
private static void checkPointArrays(float[] src, int srcIndex,
- float[] dst, int dstIndex,
- int pointCount) {
+ float[] dst, int dstIndex,
+ int pointCount) {
// check for too-small and too-big indices
int srcStop = srcIndex + (pointCount << 1);
int dstStop = dstIndex + (pointCount << 1);
@@ -589,84 +586,81 @@
}
/**
- * Set the matrix such that the specified src points would map to the
- * specified dst points. The "points" are represented as an array of floats,
- * order [x0, y0, x1, y1, ...], where each "point" is 2 float values.
+ * Set the matrix such that the specified src points would map to the specified dst points. The
+ * "points" are represented as an array of floats, order [x0, y0, x1, y1, ...], where each
+ * "point" is 2 float values.
*
- * @param src The array of src [x,y] pairs (points)
+ * @param src The array of src [x,y] pairs (points)
* @param srcIndex Index of the first pair of src values
- * @param dst The array of dst [x,y] pairs (points)
+ * @param dst The array of dst [x,y] pairs (points)
* @param dstIndex Index of the first pair of dst values
* @param pointCount The number of pairs/points to be used. Must be [0..4]
* @return true if the matrix was set to the specified transformation
*/
public boolean setPolyToPoly(float[] src, int srcIndex,
- float[] dst, int dstIndex,
- int pointCount) {
+ float[] dst, int dstIndex,
+ int pointCount) {
if (pointCount > 4) {
throw new IllegalArgumentException();
}
checkPointArrays(src, srcIndex, dst, dstIndex, pointCount);
- return native_setPolyToPoly(native_instance, src, srcIndex,
- dst, dstIndex, pointCount);
+ return nSetPolyToPoly(native_instance, src, srcIndex,
+ dst, dstIndex, pointCount);
}
/**
- * If this matrix can be inverted, return true and if inverse is not null,
- * set inverse to be the inverse of this matrix. If this matrix cannot be
- * inverted, ignore inverse and return false.
+ * If this matrix can be inverted, return true and if inverse is not null, set inverse to be the
+ * inverse of this matrix. If this matrix cannot be inverted, ignore inverse and return false.
*/
public boolean invert(Matrix inverse) {
- return native_invert(native_instance, inverse.native_instance);
+ return nInvert(native_instance, inverse.native_instance);
}
/**
- * Apply this matrix to the array of 2D points specified by src, and write
- * the transformed points into the array of points specified by dst. The
- * two arrays represent their "points" as pairs of floats [x, y].
+ * Apply this matrix to the array of 2D points specified by src, and write the transformed
+ * points into the array of points specified by dst. The two arrays represent their "points" as
+ * pairs of floats [x, y].
*
- * @param dst The array of dst points (x,y pairs)
+ * @param dst The array of dst points (x,y pairs)
* @param dstIndex The index of the first [x,y] pair of dst floats
- * @param src The array of src points (x,y pairs)
+ * @param src The array of src points (x,y pairs)
* @param srcIndex The index of the first [x,y] pair of src floats
* @param pointCount The number of points (x,y pairs) to transform
*/
public void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex,
- int pointCount) {
+ int pointCount) {
checkPointArrays(src, srcIndex, dst, dstIndex, pointCount);
- native_mapPoints(native_instance, dst, dstIndex, src, srcIndex,
- pointCount, true);
+ nMapPoints(native_instance, dst, dstIndex, src, srcIndex,
+ pointCount, true);
}
/**
- * Apply this matrix to the array of 2D vectors specified by src, and write
- * the transformed vectors into the array of vectors specified by dst. The
- * two arrays represent their "vectors" as pairs of floats [x, y].
+ * Apply this matrix to the array of 2D vectors specified by src, and write the transformed
+ * vectors into the array of vectors specified by dst. The two arrays represent their "vectors"
+ * as pairs of floats [x, y]. Note: this method does not apply the translation associated with
+ * the matrix. Use {@link Matrix#mapPoints(float[], int, float[], int, int)} if you want the
+ * translation to be applied.
*
- * Note: this method does not apply the translation associated with the matrix. Use
- * {@link Matrix#mapPoints(float[], int, float[], int, int)} if you want the translation
- * to be applied.
- *
- * @param dst The array of dst vectors (x,y pairs)
+ * @param dst The array of dst vectors (x,y pairs)
* @param dstIndex The index of the first [x,y] pair of dst floats
- * @param src The array of src vectors (x,y pairs)
+ * @param src The array of src vectors (x,y pairs)
* @param srcIndex The index of the first [x,y] pair of src floats
* @param vectorCount The number of vectors (x,y pairs) to transform
*/
public void mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex,
- int vectorCount) {
+ int vectorCount) {
checkPointArrays(src, srcIndex, dst, dstIndex, vectorCount);
- native_mapPoints(native_instance, dst, dstIndex, src, srcIndex,
- vectorCount, false);
+ nMapPoints(native_instance, dst, dstIndex, src, srcIndex,
+ vectorCount, false);
}
/**
- * Apply this matrix to the array of 2D points specified by src, and write
- * the transformed points into the array of points specified by dst. The
- * two arrays represent their "points" as pairs of floats [x, y].
+ * Apply this matrix to the array of 2D points specified by src, and write the transformed
+ * points into the array of points specified by dst. The two arrays represent their "points" as
+ * pairs of floats [x, y].
*
- * @param dst The array of dst points (x,y pairs)
- * @param src The array of src points (x,y pairs)
+ * @param dst The array of dst points (x,y pairs)
+ * @param src The array of src points (x,y pairs)
*/
public void mapPoints(float[] dst, float[] src) {
if (dst.length != src.length) {
@@ -676,15 +670,14 @@
}
/**
- * Apply this matrix to the array of 2D vectors specified by src, and write
- * the transformed vectors into the array of vectors specified by dst. The
- * two arrays represent their "vectors" as pairs of floats [x, y].
+ * Apply this matrix to the array of 2D vectors specified by src, and write the transformed
+ * vectors into the array of vectors specified by dst. The two arrays represent their "vectors"
+ * as pairs of floats [x, y]. Note: this method does not apply the translation associated with
+ * the matrix. Use {@link Matrix#mapPoints(float[], float[])} if you want the translation to be
+ * applied.
*
- * Note: this method does not apply the translation associated with the matrix. Use
- * {@link Matrix#mapPoints(float[], float[])} if you want the translation to be applied.
- *
- * @param dst The array of dst vectors (x,y pairs)
- * @param src The array of src vectors (x,y pairs)
+ * @param dst The array of dst vectors (x,y pairs)
+ * @param src The array of src vectors (x,y pairs)
*/
public void mapVectors(float[] dst, float[] src) {
if (dst.length != src.length) {
@@ -694,8 +687,8 @@
}
/**
- * Apply this matrix to the array of 2D points, and write the transformed
- * points back into the array
+ * Apply this matrix to the array of 2D points, and write the transformed points back into the
+ * array
*
* @param pts The array [x0, y0, x1, y1, ...] of points to transform.
*/
@@ -704,10 +697,8 @@
}
/**
- * Apply this matrix to the array of 2D vectors, and write the transformed
- * vectors back into the array.
- *
- * Note: this method does not apply the translation associated with the matrix. Use
+ * Apply this matrix to the array of 2D vectors, and write the transformed vectors back into the
+ * array. Note: this method does not apply the translation associated with the matrix. Use
* {@link Matrix#mapPoints(float[])} if you want the translation to be applied.
*
* @param vecs The array [x0, y0, x1, y1, ...] of vectors to transform.
@@ -717,9 +708,9 @@
}
/**
- * Apply this matrix to the src rectangle, and write the transformed
- * rectangle into dst. This is accomplished by transforming the 4 corners of
- * src, and then setting dst to the bounds of those points.
+ * Apply this matrix to the src rectangle, and write the transformed rectangle into dst. This is
+ * accomplished by transforming the 4 corners of src, and then setting dst to the bounds of
+ * those points.
*
* @param dst Where the transformed rectangle is written.
* @param src The original rectangle to be transformed.
@@ -729,13 +720,13 @@
if (dst == null || src == null) {
throw new NullPointerException();
}
- return native_mapRect(native_instance, dst, src);
+ return nMapRect(native_instance, dst, src);
}
/**
- * Apply this matrix to the rectangle, and write the transformed rectangle
- * back into it. This is accomplished by transforming the 4 corners of rect,
- * and then setting it to the bounds of those points
+ * Apply this matrix to the rectangle, and write the transformed rectangle back into it. This is
+ * accomplished by transforming the 4 corners of rect, and then setting it to the bounds of
+ * those points
*
* @param rect The rectangle to transform.
* @return the result of calling rectStaysRect()
@@ -745,34 +736,33 @@
}
/**
- * Return the mean radius of a circle after it has been mapped by
- * this matrix. NOTE: in perspective this value assumes the circle
- * has its center at the origin.
+ * Return the mean radius of a circle after it has been mapped by this matrix. NOTE: in
+ * perspective this value assumes the circle has its center at the origin.
*/
public float mapRadius(float radius) {
- return native_mapRadius(native_instance, radius);
+ return nMapRadius(native_instance, radius);
}
- /** Copy 9 values from the matrix into the array.
- */
+ /**
+ * Copy 9 values from the matrix into the array.
+ */
public void getValues(float[] values) {
if (values.length < 9) {
throw new ArrayIndexOutOfBoundsException();
}
- native_getValues(native_instance, values);
+ nGetValues(native_instance, values);
}
- /** Copy 9 values from the array into the matrix.
- Depending on the implementation of Matrix, these may be
- transformed into 16.16 integers in the Matrix, such that
- a subsequent call to getValues() will not yield exactly
- the same values.
- */
+ /**
+ * Copy 9 values from the array into the matrix. Depending on the implementation of Matrix,
+ * these may be transformed into 16.16 integers in the Matrix, such that a subsequent call to
+ * getValues() will not yield exactly the same values.
+ */
public void setValues(float[] values) {
if (values.length < 9) {
throw new ArrayIndexOutOfBoundsException();
}
- native_setValues(native_instance, values);
+ nSetValues(native_instance, values);
}
@Override
@@ -798,122 +788,155 @@
float[] values = new float[9];
getValues(values);
sb.append('[');
- sb.append(values[0]); sb.append(", "); sb.append(values[1]); sb.append(", ");
- sb.append(values[2]); sb.append("][");
- sb.append(values[3]); sb.append(", "); sb.append(values[4]); sb.append(", ");
- sb.append(values[5]); sb.append("][");
- sb.append(values[6]); sb.append(", "); sb.append(values[7]); sb.append(", ");
- sb.append(values[8]); sb.append(']');
+ sb.append(values[0]);
+ sb.append(", ");
+ sb.append(values[1]);
+ sb.append(", ");
+ sb.append(values[2]);
+ sb.append("][");
+ sb.append(values[3]);
+ sb.append(", ");
+ sb.append(values[4]);
+ sb.append(", ");
+ sb.append(values[5]);
+ sb.append("][");
+ sb.append(values[6]);
+ sb.append(", ");
+ sb.append(values[7]);
+ sb.append(", ");
+ sb.append(values[8]);
+ sb.append(']');
}
/**
* Print short string, to optimize dumping.
+ *
* @hide
*/
public void printShortString(PrintWriter pw) {
float[] values = new float[9];
getValues(values);
pw.print('[');
- pw.print(values[0]); pw.print(", "); pw.print(values[1]); pw.print(", ");
- pw.print(values[2]); pw.print("][");
- pw.print(values[3]); pw.print(", "); pw.print(values[4]); pw.print(", ");
- pw.print(values[5]); pw.print("][");
- pw.print(values[6]); pw.print(", "); pw.print(values[7]); pw.print(", ");
- pw.print(values[8]); pw.print(']');
+ pw.print(values[0]);
+ pw.print(", ");
+ pw.print(values[1]);
+ pw.print(", ");
+ pw.print(values[2]);
+ pw.print("][");
+ pw.print(values[3]);
+ pw.print(", ");
+ pw.print(values[4]);
+ pw.print(", ");
+ pw.print(values[5]);
+ pw.print("][");
+ pw.print(values[6]);
+ pw.print(", ");
+ pw.print(values[7]);
+ pw.print(", ");
+ pw.print(values[8]);
+ pw.print(']');
}
- @Override
- protected void finalize() throws Throwable {
- try {
- finalizer(native_instance);
- native_instance = 0; // Other finalizers can still call us.
- } finally {
- super.finalize();
- }
- }
-
- /*package*/ final long ni() {
+ /* package */ final long ni() {
return native_instance;
}
- private static native long native_create(long native_src_or_zero);
- private static native boolean native_isIdentity(long native_object);
- private static native boolean native_isAffine(long native_object);
- private static native boolean native_rectStaysRect(long native_object);
- private static native void native_reset(long native_object);
- private static native void native_set(long native_object,
- long native_other);
- private static native void native_setTranslate(long native_object,
- float dx, float dy);
- private static native void native_setScale(long native_object,
- float sx, float sy, float px, float py);
- private static native void native_setScale(long native_object,
- float sx, float sy);
- private static native void native_setRotate(long native_object,
- float degrees, float px, float py);
- private static native void native_setRotate(long native_object,
- float degrees);
- private static native void native_setSinCos(long native_object,
- float sinValue, float cosValue, float px, float py);
- private static native void native_setSinCos(long native_object,
- float sinValue, float cosValue);
- private static native void native_setSkew(long native_object,
- float kx, float ky, float px, float py);
- private static native void native_setSkew(long native_object,
- float kx, float ky);
- private static native void native_setConcat(long native_object,
- long native_a,
- long native_b);
- private static native void native_preTranslate(long native_object,
- float dx, float dy);
- private static native void native_preScale(long native_object,
- float sx, float sy, float px, float py);
- private static native void native_preScale(long native_object,
- float sx, float sy);
- private static native void native_preRotate(long native_object,
- float degrees, float px, float py);
- private static native void native_preRotate(long native_object,
- float degrees);
- private static native void native_preSkew(long native_object,
- float kx, float ky, float px, float py);
- private static native void native_preSkew(long native_object,
- float kx, float ky);
- private static native void native_preConcat(long native_object,
- long native_other_matrix);
- private static native void native_postTranslate(long native_object,
- float dx, float dy);
- private static native void native_postScale(long native_object,
- float sx, float sy, float px, float py);
- private static native void native_postScale(long native_object,
- float sx, float sy);
- private static native void native_postRotate(long native_object,
- float degrees, float px, float py);
- private static native void native_postRotate(long native_object,
- float degrees);
- private static native void native_postSkew(long native_object,
- float kx, float ky, float px, float py);
- private static native void native_postSkew(long native_object,
- float kx, float ky);
- private static native void native_postConcat(long native_object,
- long native_other_matrix);
- private static native boolean native_setRectToRect(long native_object,
- RectF src, RectF dst, int stf);
- private static native boolean native_setPolyToPoly(long native_object,
- float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount);
- private static native boolean native_invert(long native_object,
- long native_inverse);
- private static native void native_mapPoints(long native_object,
- float[] dst, int dstIndex, float[] src, int srcIndex,
- int ptCount, boolean isPts);
- private static native boolean native_mapRect(long native_object,
- RectF dst, RectF src);
- private static native float native_mapRadius(long native_object,
- float radius);
- private static native void native_getValues(long native_object,
- float[] values);
- private static native void native_setValues(long native_object,
- float[] values);
- private static native boolean native_equals(long native_a, long native_b);
- private static native void finalizer(long native_instance);
+ // ------------------ Regular JNI ------------------------
+
+ private static native long nCreate(long nSrc_or_zero);
+ private static native long nGetNativeFinalizer();
+
+
+ // ------------------ Fast JNI ------------------------
+
+ @FastNative
+ private static native boolean nSetRectToRect(long nObject,
+ RectF src, RectF dst, int stf);
+ @FastNative
+ private static native boolean nSetPolyToPoly(long nObject,
+ float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount);
+ @FastNative
+ private static native void nMapPoints(long nObject,
+ float[] dst, int dstIndex, float[] src, int srcIndex,
+ int ptCount, boolean isPts);
+ @FastNative
+ private static native boolean nMapRect(long nObject, RectF dst, RectF src);
+ @FastNative
+ private static native void nGetValues(long nObject, float[] values);
+ @FastNative
+ private static native void nSetValues(long nObject, float[] values);
+
+
+ // ------------------ Critical JNI ------------------------
+
+ @CriticalNative
+ private static native boolean nIsIdentity(long nObject);
+ @CriticalNative
+ private static native boolean nIsAffine(long nObject);
+ @CriticalNative
+ private static native boolean nRectStaysRect(long nObject);
+ @CriticalNative
+ private static native void nReset(long nObject);
+ @CriticalNative
+ private static native void nSet(long nObject, long nOther);
+ @CriticalNative
+ private static native void nSetTranslate(long nObject, float dx, float dy);
+ @CriticalNative
+ private static native void nSetScale(long nObject, float sx, float sy, float px, float py);
+ @CriticalNative
+ private static native void nSetScale(long nObject, float sx, float sy);
+ @CriticalNative
+ private static native void nSetRotate(long nObject, float degrees, float px, float py);
+ @CriticalNative
+ private static native void nSetRotate(long nObject, float degrees);
+ @CriticalNative
+ private static native void nSetSinCos(long nObject, float sinValue, float cosValue,
+ float px, float py);
+ @CriticalNative
+ private static native void nSetSinCos(long nObject, float sinValue, float cosValue);
+ @CriticalNative
+ private static native void nSetSkew(long nObject, float kx, float ky, float px, float py);
+ @CriticalNative
+ private static native void nSetSkew(long nObject, float kx, float ky);
+ @CriticalNative
+ private static native void nSetConcat(long nObject, long nA, long nB);
+ @CriticalNative
+ private static native void nPreTranslate(long nObject, float dx, float dy);
+ @CriticalNative
+ private static native void nPreScale(long nObject, float sx, float sy, float px, float py);
+ @CriticalNative
+ private static native void nPreScale(long nObject, float sx, float sy);
+ @CriticalNative
+ private static native void nPreRotate(long nObject, float degrees, float px, float py);
+ @CriticalNative
+ private static native void nPreRotate(long nObject, float degrees);
+ @CriticalNative
+ private static native void nPreSkew(long nObject, float kx, float ky, float px, float py);
+ @CriticalNative
+ private static native void nPreSkew(long nObject, float kx, float ky);
+ @CriticalNative
+ private static native void nPreConcat(long nObject, long nOther_matrix);
+ @CriticalNative
+ private static native void nPostTranslate(long nObject, float dx, float dy);
+ @CriticalNative
+ private static native void nPostScale(long nObject, float sx, float sy, float px, float py);
+ @CriticalNative
+ private static native void nPostScale(long nObject, float sx, float sy);
+ @CriticalNative
+ private static native void nPostRotate(long nObject, float degrees, float px, float py);
+ @CriticalNative
+ private static native void nPostRotate(long nObject, float degrees);
+ @CriticalNative
+ private static native void nPostSkew(long nObject, float kx, float ky, float px, float py);
+ @CriticalNative
+ private static native void nPostSkew(long nObject, float kx, float ky);
+ @CriticalNative
+ private static native void nPostConcat(long nObject, long nOther_matrix);
+ @CriticalNative
+ private static native boolean nInvert(long nObject, long nInverse);
+ @CriticalNative
+ private static native float nMapRadius(long nObject, float radius);
+ @CriticalNative
+ private static native boolean nEquals(long nA, long nB);
}
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 4abe50f..98d45dc 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -27,6 +27,9 @@
import com.android.internal.annotations.GuardedBy;
+import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
+
import java.util.HashMap;
import java.util.Locale;
@@ -604,8 +607,6 @@
return nGetFlags(mNativePaint);
}
- private native int nGetFlags(long paintPtr);
-
/**
* Set the paint's flags. Use the Flag enum to specific flag values.
*
@@ -615,8 +616,6 @@
nSetFlags(mNativePaint, flags);
}
- private native void nSetFlags(long paintPtr, int flags);
-
/**
* Return the paint's hinting mode. Returns either
* {@link #HINTING_OFF} or {@link #HINTING_ON}.
@@ -625,8 +624,6 @@
return nGetHinting(mNativePaint);
}
- private native int nGetHinting(long paintPtr);
-
/**
* Set the paint's hinting mode. May be either
* {@link #HINTING_OFF} or {@link #HINTING_ON}.
@@ -635,8 +632,6 @@
nSetHinting(mNativePaint, mode);
}
- private native void nSetHinting(long paintPtr, int mode);
-
/**
* Helper for getFlags(), returning true if ANTI_ALIAS_FLAG bit is set
* AntiAliasing smooths out the edges of what is being drawn, but is has
@@ -661,8 +656,6 @@
nSetAntiAlias(mNativePaint, aa);
}
- private native void nSetAntiAlias(long paintPtr, boolean aa);
-
/**
* Helper for getFlags(), returning true if DITHER_FLAG bit is set
* Dithering affects how colors that are higher precision than the device
@@ -691,8 +684,6 @@
nSetDither(mNativePaint, dither);
}
- private native void nSetDither(long paintPtr, boolean dither);
-
/**
* Helper for getFlags(), returning true if LINEAR_TEXT_FLAG bit is set
*
@@ -712,8 +703,6 @@
nSetLinearText(mNativePaint, linearText);
}
- private native void nSetLinearText(long paintPtr, boolean linearText);
-
/**
* Helper for getFlags(), returning true if SUBPIXEL_TEXT_FLAG bit is set
*
@@ -733,8 +722,6 @@
nSetSubpixelText(mNativePaint, subpixelText);
}
- private native void nSetSubpixelText(long paintPtr, boolean subpixelText);
-
/**
* Helper for getFlags(), returning true if UNDERLINE_TEXT_FLAG bit is set
*
@@ -754,8 +741,6 @@
nSetUnderlineText(mNativePaint, underlineText);
}
- private native void nSetUnderlineText(long paintPtr, boolean underlineText);
-
/**
* Helper for getFlags(), returning true if STRIKE_THRU_TEXT_FLAG bit is set
*
@@ -775,8 +760,6 @@
nSetStrikeThruText(mNativePaint, strikeThruText);
}
- private native void nSetStrikeThruText(long paintPtr, boolean strikeThruText);
-
/**
* Helper for getFlags(), returning true if FAKE_BOLD_TEXT_FLAG bit is set
*
@@ -796,8 +779,6 @@
nSetFakeBoldText(mNativePaint, fakeBoldText);
}
- private native void nSetFakeBoldText(long paintPtr, boolean fakeBoldText);
-
/**
* Whether or not the bitmap filter is activated.
* Filtering affects the sampling of bitmaps when they are transformed.
@@ -823,8 +804,6 @@
nSetFilterBitmap(mNativePaint, filter);
}
- private native void nSetFilterBitmap(long paintPtr, boolean filter);
-
/**
* Return the paint's style, used for controlling how primitives'
* geometries are interpreted (except for drawBitmap, which always assumes
@@ -860,8 +839,6 @@
return nGetColor(mNativePaint);
}
- private native int nGetColor(long paintPtr);
-
/**
* Set the paint's color. Note that the color is an int containing alpha
* as well as r,g,b. This 32bit value is not premultiplied, meaning that
@@ -874,8 +851,6 @@
nSetColor(mNativePaint, color);
}
- private native void nSetColor(long paintPtr, @ColorInt int color);
-
/**
* Helper to getColor() that just returns the color's alpha value. This is
* the same as calling getColor() >>> 24. It always returns a value between
@@ -887,8 +862,6 @@
return nGetAlpha(mNativePaint);
}
- private native int nGetAlpha(long paintPtr);
-
/**
* Helper to setColor(), that only assigns the color's alpha value,
* leaving its r,g,b values unchanged. Results are undefined if the alpha
@@ -900,8 +873,6 @@
nSetAlpha(mNativePaint, a);
}
- private native void nSetAlpha(long paintPtr, int a);
-
/**
* Helper to setColor(), that takes a,r,g,b and constructs the color int
*
@@ -927,8 +898,6 @@
return nGetStrokeWidth(mNativePaint);
}
- private native float nGetStrokeWidth(long paintPtr);
-
/**
* Set the width for stroking.
* Pass 0 to stroke in hairline mode.
@@ -941,8 +910,6 @@
nSetStrokeWidth(mNativePaint, width);
}
- private native void nSetStrokeWidth(long paintPtr, float width);
-
/**
* Return the paint's stroke miter value. Used to control the behavior
* of miter joins when the joins angle is sharp.
@@ -954,8 +921,6 @@
return nGetStrokeMiter(mNativePaint);
}
- private native float nGetStrokeMiter(long paintPtr);
-
/**
* Set the paint's stroke miter value. This is used to control the behavior
* of miter joins when the joins angle is sharp. This value must be >= 0.
@@ -967,8 +932,6 @@
nSetStrokeMiter(mNativePaint, miter);
}
- private native void nSetStrokeMiter(long paintPtr, float miter);
-
/**
* Return the paint's Cap, controlling how the start and end of stroked
* lines and paths are treated.
@@ -1387,8 +1350,6 @@
return nIsElegantTextHeight(mNativePaint);
}
- private native boolean nIsElegantTextHeight(long paintPtr);
-
/**
* Set the paint's elegant height metrics flag. This setting selects font
* variants that have not been compacted to fit Latin-based vertical
@@ -1400,8 +1361,6 @@
nSetElegantTextHeight(mNativePaint, elegant);
}
- private native void nSetElegantTextHeight(long paintPtr, boolean elegant);
-
/**
* Return the paint's text size.
*
@@ -1411,8 +1370,6 @@
return nGetTextSize(mNativePaint);
}
- private native float nGetTextSize(long paintPtr);
-
/**
* Set the paint's text size. This value must be > 0
*
@@ -1422,8 +1379,6 @@
nSetTextSize(mNativePaint, textSize);
}
- private native void nSetTextSize(long paintPtr, float textSize);
-
/**
* Return the paint's horizontal scale factor for text. The default value
* is 1.0.
@@ -1434,8 +1389,6 @@
return nGetTextScaleX(mNativePaint);
}
- private native float nGetTextScaleX(long paintPtr);
-
/**
* Set the paint's horizontal scale factor for text. The default value
* is 1.0. Values > 1.0 will stretch the text wider. Values < 1.0 will
@@ -1447,8 +1400,6 @@
nSetTextScaleX(mNativePaint, scaleX);
}
- private native void nSetTextScaleX(long paintPtr, float scaleX);
-
/**
* Return the paint's horizontal skew factor for text. The default value
* is 0.
@@ -1459,8 +1410,6 @@
return nGetTextSkewX(mNativePaint);
}
- private native float nGetTextSkewX(long paintPtr);
-
/**
* Set the paint's horizontal skew factor for text. The default value
* is 0. For approximating oblique text, use values around -0.25.
@@ -1471,8 +1420,6 @@
nSetTextSkewX(mNativePaint, skewX);
}
- private native void nSetTextSkewX(long paintPtr, float skewX);
-
/**
* Return the paint's letter-spacing for text. The default value
* is 0.
@@ -1565,8 +1512,6 @@
return nAscent(mNativePaint, mNativeTypeface);
}
- private native float nAscent(long paintPtr, long typefacePtr);
-
/**
* Return the distance below (positive) the baseline (descent) based on the
* current typeface and text size.
@@ -1578,8 +1523,6 @@
return nDescent(mNativePaint, mNativeTypeface);
}
- private native float nDescent(long paintPtr, long typefacePtr);
-
/**
* Class that describes the various metrics for a font at a given text size.
* Remember, Y values increase going down, so those values will be positive,
@@ -1624,9 +1567,6 @@
return nGetFontMetrics(mNativePaint, mNativeTypeface, metrics);
}
- private native float nGetFontMetrics(long paintPtr,
- long typefacePtr, FontMetrics metrics);
-
/**
* Allocates a new FontMetrics object, and then calls getFontMetrics(fm)
* with it, returning the object.
@@ -1686,9 +1626,6 @@
return nGetFontMetricsInt(mNativePaint, mNativeTypeface, fmi);
}
- private native int nGetFontMetricsInt(long paintPtr,
- long typefacePtr, FontMetricsInt fmi);
-
public FontMetricsInt getFontMetricsInt() {
FontMetricsInt fm = new FontMetricsInt();
getFontMetricsInt(fm);
@@ -1860,10 +1797,6 @@
return res;
}
- private static native int nBreakText(long nObject, long nTypeface,
- char[] text, int index, int count,
- float maxWidth, int bidiFlags, float[] measuredWidth);
-
/**
* Measure the text, stopping early if the measured width exceeds maxWidth.
* Return the number of chars that were measured, and if measuredWidth is
@@ -1952,10 +1885,6 @@
return res;
}
- private static native int nBreakText(long nObject, long nTypeface,
- String text, boolean measureForwards,
- float maxWidth, int bidiFlags, float[] measuredWidth);
-
/**
* Return the advance widths for the characters in the string.
*
@@ -2679,73 +2608,34 @@
return result;
}
+ // regular JNI
+ private static native long nGetNativeFinalizer();
private static native long nInit();
private static native long nInitWithPaint(long paint);
- private static native void nReset(long paintPtr);
- private static native void nSet(long paintPtrDest, long paintPtrSrc);
- private static native int nGetStyle(long paintPtr);
- private static native void nSetStyle(long paintPtr, int style);
- private static native int nGetStrokeCap(long paintPtr);
- private static native void nSetStrokeCap(long paintPtr, int cap);
- private static native int nGetStrokeJoin(long paintPtr);
- private static native void nSetStrokeJoin(long paintPtr,
- int join);
- private static native boolean nGetFillPath(long paintPtr,
- long src, long dst);
- private static native long nSetShader(long paintPtr, long shader);
- private static native long nSetColorFilter(long paintPtr,
- long filter);
- private static native void nSetXfermode(long paintPtr, int xfermode);
- private static native long nSetPathEffect(long paintPtr,
- long effect);
- private static native long nSetMaskFilter(long paintPtr,
- long maskfilter);
- private static native long nSetTypeface(long paintPtr,
- long typeface);
- private static native long nSetRasterizer(long paintPtr,
- long rasterizer);
-
- private static native int nGetTextAlign(long paintPtr);
- private static native void nSetTextAlign(long paintPtr,
- int align);
-
- private static native int nSetTextLocales(long paintPtr, String locales);
- private static native void nSetTextLocalesByMinikinLangListId(long paintPtr,
- int mMinikinLangListId);
-
+ private static native int nBreakText(long nObject, long nTypeface,
+ char[] text, int index, int count,
+ float maxWidth, int bidiFlags, float[] measuredWidth);
+ private static native int nBreakText(long nObject, long nTypeface,
+ String text, boolean measureForwards,
+ float maxWidth, int bidiFlags, float[] measuredWidth);
private static native float nGetTextAdvances(long paintPtr, long typefacePtr,
char[] text, int index, int count, int contextIndex, int contextCount,
int bidiFlags, float[] advances, int advancesIndex);
private static native float nGetTextAdvances(long paintPtr, long typefacePtr,
String text, int start, int end, int contextStart, int contextEnd,
int bidiFlags, float[] advances, int advancesIndex);
-
private native int nGetTextRunCursor(long paintPtr, char[] text,
int contextStart, int contextLength, int dir, int offset, int cursorOpt);
private native int nGetTextRunCursor(long paintPtr, String text,
int contextStart, int contextEnd, int dir, int offset, int cursorOpt);
-
private static native void nGetTextPath(long paintPtr, long typefacePtr,
int bidiFlags, char[] text, int index, int count, float x, float y, long path);
private static native void nGetTextPath(long paintPtr, long typefacePtr,
int bidiFlags, String text, int start, int end, float x, float y, long path);
private static native void nGetStringBounds(long nativePaint, long typefacePtr,
- String text, int start, int end, int bidiFlags, Rect bounds);
+ String text, int start, int end, int bidiFlags, Rect bounds);
private static native void nGetCharArrayBounds(long nativePaint, long typefacePtr,
- char[] text, int index, int count, int bidiFlags, Rect bounds);
- private static native long nGetNativeFinalizer();
-
- private static native void nSetShadowLayer(long paintPtr,
- float radius, float dx, float dy, int color);
- private static native boolean nHasShadowLayer(long paintPtr);
-
- private static native float nGetLetterSpacing(long paintPtr);
- private static native void nSetLetterSpacing(long paintPtr,
- float letterSpacing);
- private static native void nSetFontFeatureSettings(long paintPtr,
- String settings);
- private static native int nGetHyphenEdit(long paintPtr);
- private static native void nSetHyphenEdit(long paintPtr, int hyphen);
+ char[] text, int index, int count, int bidiFlags, Rect bounds);
private static native boolean nHasGlyph(long paintPtr, long typefacePtr,
int bidiFlags, String string);
private static native float nGetRunAdvance(long paintPtr, long typefacePtr,
@@ -2754,4 +2644,134 @@
private static native int nGetOffsetForAdvance(long paintPtr,
long typefacePtr, char[] text, int start, int end, int contextStart, int contextEnd,
boolean isRtl, float advance);
+
+
+ // ---------------- @FastNative ------------------------
+
+ @FastNative
+ private static native int nSetTextLocales(long paintPtr, String locales);
+ @FastNative
+ private static native void nSetFontFeatureSettings(long paintPtr, String settings);
+ @FastNative
+ private static native float nGetFontMetrics(long paintPtr,
+ long typefacePtr, FontMetrics metrics);
+ @FastNative
+ private static native int nGetFontMetricsInt(long paintPtr,
+ long typefacePtr, FontMetricsInt fmi);
+
+
+ // ---------------- @CriticalNative ------------------------
+
+ @CriticalNative
+ private static native void nReset(long paintPtr);
+ @CriticalNative
+ private static native void nSet(long paintPtrDest, long paintPtrSrc);
+ @CriticalNative
+ private static native int nGetStyle(long paintPtr);
+ @CriticalNative
+ private static native void nSetStyle(long paintPtr, int style);
+ @CriticalNative
+ private static native int nGetStrokeCap(long paintPtr);
+ @CriticalNative
+ private static native void nSetStrokeCap(long paintPtr, int cap);
+ @CriticalNative
+ private static native int nGetStrokeJoin(long paintPtr);
+ @CriticalNative
+ private static native void nSetStrokeJoin(long paintPtr, int join);
+ @CriticalNative
+ private static native boolean nGetFillPath(long paintPtr, long src, long dst);
+ @CriticalNative
+ private static native long nSetShader(long paintPtr, long shader);
+ @CriticalNative
+ private static native long nSetColorFilter(long paintPtr, long filter);
+ @CriticalNative
+ private static native void nSetXfermode(long paintPtr, int xfermode);
+ @CriticalNative
+ private static native long nSetPathEffect(long paintPtr, long effect);
+ @CriticalNative
+ private static native long nSetMaskFilter(long paintPtr, long maskfilter);
+ @CriticalNative
+ private static native long nSetTypeface(long paintPtr, long typeface);
+ @CriticalNative
+ private static native long nSetRasterizer(long paintPtr, long rasterizer);
+ @CriticalNative
+ private static native int nGetTextAlign(long paintPtr);
+ @CriticalNative
+ private static native void nSetTextAlign(long paintPtr, int align);
+ @CriticalNative
+ private static native void nSetTextLocalesByMinikinLangListId(long paintPtr,
+ int mMinikinLangListId);
+ @CriticalNative
+ private static native void nSetShadowLayer(long paintPtr,
+ float radius, float dx, float dy, int color);
+ @CriticalNative
+ private static native boolean nHasShadowLayer(long paintPtr);
+ @CriticalNative
+ private static native float nGetLetterSpacing(long paintPtr);
+ @CriticalNative
+ private static native void nSetLetterSpacing(long paintPtr, float letterSpacing);
+ @CriticalNative
+ private static native int nGetHyphenEdit(long paintPtr);
+ @CriticalNative
+ private static native void nSetHyphenEdit(long paintPtr, int hyphen);
+ @CriticalNative
+ private static native void nSetStrokeMiter(long paintPtr, float miter);
+ @CriticalNative
+ private static native float nGetStrokeMiter(long paintPtr);
+ @CriticalNative
+ private static native void nSetStrokeWidth(long paintPtr, float width);
+ @CriticalNative
+ private static native float nGetStrokeWidth(long paintPtr);
+ @CriticalNative
+ private static native void nSetAlpha(long paintPtr, int a);
+ @CriticalNative
+ private static native void nSetDither(long paintPtr, boolean dither);
+ @CriticalNative
+ private static native int nGetFlags(long paintPtr);
+ @CriticalNative
+ private static native void nSetFlags(long paintPtr, int flags);
+ @CriticalNative
+ private static native int nGetHinting(long paintPtr);
+ @CriticalNative
+ private static native void nSetHinting(long paintPtr, int mode);
+ @CriticalNative
+ private static native void nSetAntiAlias(long paintPtr, boolean aa);
+ @CriticalNative
+ private static native void nSetLinearText(long paintPtr, boolean linearText);
+ @CriticalNative
+ private static native void nSetSubpixelText(long paintPtr, boolean subpixelText);
+ @CriticalNative
+ private static native void nSetUnderlineText(long paintPtr, boolean underlineText);
+ @CriticalNative
+ private static native void nSetFakeBoldText(long paintPtr, boolean fakeBoldText);
+ @CriticalNative
+ private static native void nSetFilterBitmap(long paintPtr, boolean filter);
+ @CriticalNative
+ private static native int nGetColor(long paintPtr);
+ @CriticalNative
+ private static native void nSetColor(long paintPtr, @ColorInt int color);
+ @CriticalNative
+ private static native int nGetAlpha(long paintPtr);
+ @CriticalNative
+ private static native void nSetStrikeThruText(long paintPtr, boolean strikeThruText);
+ @CriticalNative
+ private static native boolean nIsElegantTextHeight(long paintPtr);
+ @CriticalNative
+ private static native void nSetElegantTextHeight(long paintPtr, boolean elegant);
+ @CriticalNative
+ private static native float nGetTextSize(long paintPtr);
+ @CriticalNative
+ private static native float nGetTextScaleX(long paintPtr);
+ @CriticalNative
+ private static native void nSetTextScaleX(long paintPtr, float scaleX);
+ @CriticalNative
+ private static native float nGetTextSkewX(long paintPtr);
+ @CriticalNative
+ private static native void nSetTextSkewX(long paintPtr, float skewX);
+ @CriticalNative
+ private static native float nAscent(long paintPtr, long typefacePtr);
+ @CriticalNative
+ private static native float nDescent(long paintPtr, long typefacePtr);
+ @CriticalNative
+ private static native void nSetTextSize(long paintPtr, float textSize);
}
diff --git a/include/androidfw/AssetManager.h b/include/androidfw/AssetManager.h
index 0b22802..3b5cdce 100644
--- a/include/androidfw/AssetManager.h
+++ b/include/androidfw/AssetManager.h
@@ -59,10 +59,7 @@
* single instance may be shared across multiple threads, and a single
* thread may have more than one instance (the latter is discouraged).
*
- * The purpose of the AssetManager is to create Asset objects. To do
- * this efficiently it may cache information about the locations of
- * files it has seen. This can be controlled with the "cacheMode"
- * argument.
+ * The purpose of the AssetManager is to create Asset objects.
*
* The asset hierarchy may be examined like a filesystem, using
* AssetDir objects to peruse a single directory.
@@ -73,24 +70,16 @@
static const char* IDMAP_BIN;
static const char* OVERLAY_DIR;
/*
- * If OVERLAY_SUBDIR_PROPERTY is set, search for runtime resource overlay
- * APKs in OVERLAY_SUBDIR/<value of OVERLAY_SUBDIR_PROPERTY>/ rather than in
+ * If OVERLAY_SKU_DIR_PROPERTY is set, search for runtime resource overlay
+ * APKs in OVERLAY_DIR/<value of OVERLAY_SKU_DIR_PROPERTY> rather than in
* OVERLAY_DIR.
*/
- static const char* OVERLAY_SUBDIR;
- static const char* OVERLAY_SUBDIR_PROPERTY;
+ static const char* OVERLAY_SKU_DIR_PROPERTY;
static const char* TARGET_PACKAGE_NAME;
static const char* TARGET_APK_PATH;
static const char* IDMAP_DIR;
- typedef enum CacheMode {
- CACHE_UNKNOWN = 0,
- CACHE_OFF, // don't try to cache file locations
- CACHE_DEFER, // construct cache as pieces are needed
- //CACHE_SCAN, // scan full(!) asset hierarchy at init() time
- } CacheMode;
-
- AssetManager(CacheMode cacheMode = CACHE_OFF);
+ AssetManager();
virtual ~AssetManager(void);
static int32_t getGlobalCount();
@@ -127,23 +116,16 @@
int32_t nextAssetPath(const int32_t cookie) const;
/*
- * Return an asset path in the manager. 'which' must be between 0 and
- * countAssetPaths().
+ * Return an asset path in the manager. 'cookie' must be a non-negative value
+ * previously returned from addAssetPath() or nextAssetPath().
*/
String8 getAssetPath(const int32_t cookie) const;
/*
- * Set the current locale and vendor. The locale can change during
- * the lifetime of an AssetManager if the user updates the device's
- * language setting. The vendor is less likely to change.
- *
- * Pass in NULL to indicate no preference.
- */
- void setLocale(const char* locale);
- void setVendor(const char* vendor);
-
- /*
- * Choose screen orientation for resources values returned.
+ * Sets various device configuration parameters, like screen orientation, layout,
+ * size, locale, etc.
+ * The optional 'locale' string takes precedence over the locale within 'config'
+ * and must be in bcp47 format.
*/
void setConfiguration(const ResTable_config& config, const char* locale = NULL);
@@ -154,9 +136,6 @@
/*
* Open an asset.
*
- * This will search through locale-specific and vendor-specific
- * directories and packages to find the file.
- *
* The object returned does not depend on the AssetManager. It should
* be freed by calling Asset::close().
*/
@@ -166,9 +145,8 @@
* Open a non-asset file as an asset.
*
* This is for opening files that are included in an asset package
- * but aren't assets. These sit outside the usual "locale/vendor"
- * path hierarchy, and will not be seen by "AssetDir" or included
- * in our filename cache.
+ * but aren't assets. These sit outside the usual "assets/"
+ * path hierarchy, and will not be seen by "AssetDir".
*/
Asset* openNonAsset(const char* fileName, AccessMode mode, int32_t* outCookie = NULL);
@@ -181,11 +159,6 @@
/*
* Open a directory within the asset hierarchy.
*
- * The contents of the directory are an amalgam of vendor-specific,
- * locale-specific, and generic assets stored loosely or in asset
- * packages. Depending on the cache setting and previous accesses,
- * this call may incur significant disk overhead.
- *
* To open the top-level directory, pass in "".
*/
AssetDir* openDir(const char* dirName);
@@ -193,11 +166,6 @@
/*
* Open a directory within a particular path of the asset manager.
*
- * The contents of the directory are an amalgam of vendor-specific,
- * locale-specific, and generic assets stored loosely or in asset
- * packages. Depending on the cache setting and previous accesses,
- * this call may incur significant disk overhead.
- *
* To open the top-level directory, pass in "".
*/
AssetDir* openNonAssetDir(const int32_t cookie, const char* dirName);
@@ -216,13 +184,6 @@
const ResTable& getResources(bool required = true) const;
/*
- * Discard cached filename information. This only needs to be called
- * if somebody has updated the set of "loose" files, and we want to
- * discard our cached notion of what's where.
- */
- void purge(void) { purgeFileNameCacheLocked(); }
-
- /*
* Return true if the files this AssetManager references are all
* up-to-date (have not been changed since it was created). If false
* is returned, you will need to create a new AssetManager to get
@@ -254,14 +215,8 @@
bool isSystemAsset;
};
- Asset* openInPathLocked(const char* fileName, AccessMode mode,
- const asset_path& path);
Asset* openNonAssetInPathLocked(const char* fileName, AccessMode mode,
const asset_path& path);
- Asset* openInLocaleVendorLocked(const char* fileName, AccessMode mode,
- const asset_path& path, const char* locale, const char* vendor);
- String8 createPathNameLocked(const asset_path& path, const char* locale,
- const char* vendor);
String8 createPathNameLocked(const asset_path& path, const char* rootDir);
String8 createZipSourceNameLocked(const String8& zipFileName,
const String8& dirName, const String8& fileName);
@@ -279,15 +234,6 @@
void mergeInfoLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo,
const SortedVector<AssetDir::FileInfo>* pContents);
- void loadFileNameCacheLocked(void);
- void fncScanLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo,
- const char* dirName);
- bool fncScanAndMergeDirLocked(
- SortedVector<AssetDir::FileInfo>* pMergedInfo,
- const asset_path& path, const char* locale, const char* vendor,
- const char* dirName);
- void purgeFileNameCacheLocked(void);
-
const ResTable* getResTable(bool required = true) const;
void setLocaleLocked(const char* locale);
void updateResourceParamsLocked() const;
@@ -344,8 +290,8 @@
*/
class ZipSet {
public:
- ZipSet(void);
- ~ZipSet(void);
+ ZipSet() = default;
+ ~ZipSet();
/*
* Return a ZipFileRO structure for a ZipFileRO with the specified
@@ -382,23 +328,9 @@
Vector<asset_path> mAssetPaths;
char* mLocale;
- char* mVendor;
mutable ResTable* mResources;
ResTable_config* mConfig;
-
- /*
- * Cached data for "loose" files. This lets us avoid poking at the
- * filesystem when searching for loose assets. Each entry is the
- * "extended partial" path, e.g. "default/default/foo/bar.txt". The
- * full set of files is present, including ".EXCLUDE" entries.
- *
- * We do not cache directory names. We don't retain the ".gz",
- * because to our clients "foo" and "foo.gz" both look like "foo".
- */
- CacheMode mCacheMode; // is the cache enabled?
- bool mCacheValid; // clear when locale or vendor changes
- SortedVector<AssetDir::FileInfo> mCache;
};
}; // namespace android
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index fe78cef..796cb36 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -56,12 +56,6 @@
static const bool kIsDebug = false;
-/*
- * Names for default app, locale, and vendor. We might want to change
- * these to be an actual locale, e.g. always use en-US as the default.
- */
-static const char* kDefaultLocale = "default";
-static const char* kDefaultVendor = "default";
static const char* kAssetsRoot = "assets";
static const char* kAppZipName = NULL; //"classes.jar";
static const char* kSystemAssets = "framework/framework-res.apk";
@@ -76,75 +70,70 @@
const char* AssetManager::RESOURCES_FILENAME = "resources.arsc";
const char* AssetManager::IDMAP_BIN = "/system/bin/idmap";
const char* AssetManager::OVERLAY_DIR = "/vendor/overlay";
+const char* AssetManager::OVERLAY_SKU_DIR_PROPERTY = "ro.boot.vendor.overlay.sku";
const char* AssetManager::TARGET_PACKAGE_NAME = "android";
const char* AssetManager::TARGET_APK_PATH = "/system/framework/framework-res.apk";
-const char* AssetManager::OVERLAY_SUBDIR = "/system/vendor/overlay-subdir";
-const char* AssetManager::OVERLAY_SUBDIR_PROPERTY = "ro.boot.vendor.overlay.subdir";
const char* AssetManager::IDMAP_DIR = "/data/resource-cache";
namespace {
- String8 idmapPathForPackagePath(const String8& pkgPath)
- {
- const char* root = getenv("ANDROID_DATA");
- LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_DATA not set");
- String8 path(root);
- path.appendPath(kResourceCache);
- char buf[256]; // 256 chars should be enough for anyone...
- strncpy(buf, pkgPath.string(), 255);
- buf[255] = '\0';
- char* filename = buf;
- while (*filename && *filename == '/') {
- ++filename;
- }
- char* p = filename;
- while (*p) {
- if (*p == '/') {
- *p = '@';
- }
- ++p;
- }
- path.appendPath(filename);
- path.append("@idmap");
+String8 idmapPathForPackagePath(const String8& pkgPath) {
+ const char* root = getenv("ANDROID_DATA");
+ LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_DATA not set");
+ String8 path(root);
+ path.appendPath(kResourceCache);
- return path;
+ char buf[256]; // 256 chars should be enough for anyone...
+ strncpy(buf, pkgPath.string(), 255);
+ buf[255] = '\0';
+ char* filename = buf;
+ while (*filename && *filename == '/') {
+ ++filename;
}
-
- /*
- * Like strdup(), but uses C++ "new" operator instead of malloc.
- */
- static char* strdupNew(const char* str)
- {
- char* newStr;
- int len;
-
- if (str == NULL)
- return NULL;
-
- len = strlen(str);
- newStr = new char[len+1];
- memcpy(newStr, str, len+1);
-
- return newStr;
+ char* p = filename;
+ while (*p) {
+ if (*p == '/') {
+ *p = '@';
+ }
+ ++p;
}
+ path.appendPath(filename);
+ path.append("@idmap");
+
+ return path;
}
/*
+ * Like strdup(), but uses C++ "new" operator instead of malloc.
+ */
+static char* strdupNew(const char* str) {
+ char* newStr;
+ int len;
+
+ if (str == NULL)
+ return NULL;
+
+ len = strlen(str);
+ newStr = new char[len+1];
+ memcpy(newStr, str, len+1);
+
+ return newStr;
+}
+
+} // namespace
+
+/*
* ===========================================================================
* AssetManager
* ===========================================================================
*/
-int32_t AssetManager::getGlobalCount()
-{
+int32_t AssetManager::getGlobalCount() {
return gCount;
}
-AssetManager::AssetManager(CacheMode cacheMode)
- : mLocale(NULL), mVendor(NULL),
- mResources(NULL), mConfig(new ResTable_config),
- mCacheMode(cacheMode), mCacheValid(false)
-{
+AssetManager::AssetManager() :
+ mLocale(NULL), mResources(NULL), mConfig(new ResTable_config) {
int count = android_atomic_inc(&gCount) + 1;
if (kIsDebug) {
ALOGI("Creating AssetManager %p #%d\n", this, count);
@@ -152,8 +141,7 @@
memset(mConfig, 0, sizeof(ResTable_config));
}
-AssetManager::~AssetManager(void)
-{
+AssetManager::~AssetManager() {
int count = android_atomic_dec(&gCount);
if (kIsDebug) {
ALOGI("Destroying AssetManager in %p #%d\n", this, count);
@@ -164,12 +152,10 @@
// don't have a String class yet, so make sure we clean up
delete[] mLocale;
- delete[] mVendor;
}
bool AssetManager::addAssetPath(
- const String8& path, int32_t* cookie, bool appAsLib, bool isSystemAsset)
-{
+ const String8& path, int32_t* cookie, bool appAsLib, bool isSystemAsset) {
AutoMutex _l(mLock);
asset_path ap;
@@ -344,25 +330,9 @@
return String8();
}
-/*
- * Set the current locale. Use NULL to indicate no locale.
- *
- * Close and reopen Zip archives as appropriate, and reset cached
- * information in the locale-specific sections of the tree.
- */
-void AssetManager::setLocale(const char* locale)
-{
- AutoMutex _l(mLock);
- setLocaleLocked(locale);
-}
-
-
void AssetManager::setLocaleLocked(const char* locale)
{
if (mLocale != NULL) {
- /* previously set, purge cached data */
- purgeFileNameCacheLocked();
- //mZipSet.purgeLocale();
delete[] mLocale;
}
@@ -370,25 +340,6 @@
updateResourceParamsLocked();
}
-/*
- * Set the current vendor. Use NULL to indicate no vendor.
- *
- * Close and reopen Zip archives as appropriate, and reset cached
- * information in the vendor-specific sections of the tree.
- */
-void AssetManager::setVendor(const char* vendor)
-{
- AutoMutex _l(mLock);
-
- if (mVendor != NULL) {
- /* previously set, purge cached data */
- purgeFileNameCacheLocked();
- //mZipSet.purgeVendor();
- delete[] mVendor;
- }
- mVendor = strdupNew(vendor);
-}
-
void AssetManager::setConfiguration(const ResTable_config& config, const char* locale)
{
AutoMutex _l(mLock);
@@ -413,23 +364,11 @@
/*
* Open an asset.
*
- * The data could be;
- * - In a file on disk (assetBase + fileName).
- * - In a compressed file on disk (assetBase + fileName.gz).
- * - In a Zip archive, uncompressed or compressed.
+ * The data could be in any asset path. Each asset path could be:
+ * - A directory on disk.
+ * - A Zip archive, uncompressed or compressed.
*
- * It can be in a number of different directories and Zip archives.
- * The search order is:
- * - [appname]
- * - locale + vendor
- * - "default" + vendor
- * - locale + "default"
- * - "default + "default"
- * - "common"
- * - (same as above)
- *
- * To find a particular file, we have to try up to eight paths with
- * all three forms of data.
+ * If the file is in a directory, it could have a .gz suffix, meaning it is compressed.
*
* We should probably reject requests for "illegal" filenames, e.g. those
* with illegal characters or "../" backward relative paths.
@@ -440,10 +379,6 @@
LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
-
- if (mCacheMode != CACHE_OFF && !mCacheValid)
- loadFileNameCacheLocked();
-
String8 assetName(kAssetsRoot);
assetName.appendPath(fileName);
@@ -468,8 +403,7 @@
/*
* Open a non-asset file as if it were an asset.
*
- * The "fileName" is the partial path starting from the application
- * name.
+ * The "fileName" is the partial path starting from the application name.
*/
Asset* AssetManager::openNonAsset(const char* fileName, AccessMode mode, int32_t* outCookie)
{
@@ -477,10 +411,6 @@
LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
-
- if (mCacheMode != CACHE_OFF && !mCacheValid)
- loadFileNameCacheLocked();
-
/*
* For each top-level asset path, search for the asset.
*/
@@ -508,9 +438,6 @@
LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
- if (mCacheMode != CACHE_OFF && !mCacheValid)
- loadFileNameCacheLocked();
-
if (which < mAssetPaths.size()) {
ALOGV("Looking for non-asset '%s' in '%s'\n", fileName,
mAssetPaths.itemAt(which).path.string());
@@ -542,10 +469,11 @@
pAsset = open(fileName, Asset::ACCESS_STREAMING);
delete pAsset;
- if (pAsset == NULL)
+ if (pAsset == NULL) {
return kFileTypeNonexistent;
- else
+ } else {
return kFileTypeRegular;
+ }
}
bool AssetManager::appendPathToResTable(const asset_path& ap, bool appAsLib) const {
@@ -660,10 +588,6 @@
LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
}
- if (mCacheMode != CACHE_OFF && !mCacheValid) {
- const_cast<AssetManager*>(this)->loadFileNameCacheLocked();
- }
-
mResources = new ResTable();
updateResourceParamsLocked();
@@ -834,158 +758,6 @@
}
/*
- * Open an asset, searching for it in the directory hierarchy for the
- * specified app.
- *
- * Pass in a NULL values for "appName" if the common app directory should
- * be used.
- */
-Asset* AssetManager::openInPathLocked(const char* fileName, AccessMode mode,
- const asset_path& ap)
-{
- Asset* pAsset = NULL;
-
- /*
- * Try various combinations of locale and vendor.
- */
- if (mLocale != NULL && mVendor != NULL)
- pAsset = openInLocaleVendorLocked(fileName, mode, ap, mLocale, mVendor);
- if (pAsset == NULL && mVendor != NULL)
- pAsset = openInLocaleVendorLocked(fileName, mode, ap, NULL, mVendor);
- if (pAsset == NULL && mLocale != NULL)
- pAsset = openInLocaleVendorLocked(fileName, mode, ap, mLocale, NULL);
- if (pAsset == NULL)
- pAsset = openInLocaleVendorLocked(fileName, mode, ap, NULL, NULL);
-
- return pAsset;
-}
-
-/*
- * Open an asset, searching for it in the directory hierarchy for the
- * specified locale and vendor.
- *
- * We also search in "app.jar".
- *
- * Pass in NULL values for "appName", "locale", and "vendor" if the
- * defaults should be used.
- */
-Asset* AssetManager::openInLocaleVendorLocked(const char* fileName, AccessMode mode,
- const asset_path& ap, const char* locale, const char* vendor)
-{
- Asset* pAsset = NULL;
-
- if (ap.type == kFileTypeDirectory) {
- if (mCacheMode == CACHE_OFF) {
- /* look at the filesystem on disk */
- String8 path(createPathNameLocked(ap, locale, vendor));
- path.appendPath(fileName);
-
- String8 excludeName(path);
- excludeName.append(kExcludeExtension);
- if (::getFileType(excludeName.string()) != kFileTypeNonexistent) {
- /* say no more */
- //printf("+++ excluding '%s'\n", (const char*) excludeName);
- return kExcludedAsset;
- }
-
- pAsset = openAssetFromFileLocked(path, mode);
-
- if (pAsset == NULL) {
- /* try again, this time with ".gz" */
- path.append(".gz");
- pAsset = openAssetFromFileLocked(path, mode);
- }
-
- if (pAsset != NULL)
- pAsset->setAssetSource(path);
- } else {
- /* find in cache */
- String8 path(createPathNameLocked(ap, locale, vendor));
- path.appendPath(fileName);
-
- AssetDir::FileInfo tmpInfo;
- bool found = false;
-
- String8 excludeName(path);
- excludeName.append(kExcludeExtension);
-
- if (mCache.indexOf(excludeName) != NAME_NOT_FOUND) {
- /* go no farther */
- //printf("+++ Excluding '%s'\n", (const char*) excludeName);
- return kExcludedAsset;
- }
-
- /*
- * File compression extensions (".gz") don't get stored in the
- * name cache, so we have to try both here.
- */
- if (mCache.indexOf(path) != NAME_NOT_FOUND) {
- found = true;
- pAsset = openAssetFromFileLocked(path, mode);
- if (pAsset == NULL) {
- /* try again, this time with ".gz" */
- path.append(".gz");
- pAsset = openAssetFromFileLocked(path, mode);
- }
- }
-
- if (pAsset != NULL)
- pAsset->setAssetSource(path);
-
- /*
- * Don't continue the search into the Zip files. Our cached info
- * said it was a file on disk; to be consistent with openDir()
- * we want to return the loose asset. If the cached file gets
- * removed, we fail.
- *
- * The alternative is to update our cache when files get deleted,
- * or make some sort of "best effort" promise, but for now I'm
- * taking the hard line.
- */
- if (found) {
- if (pAsset == NULL)
- ALOGD("Expected file not found: '%s'\n", path.string());
- return pAsset;
- }
- }
- }
-
- /*
- * Either it wasn't found on disk or on the cached view of the disk.
- * Dig through the currently-opened set of Zip files. If caching
- * is disabled, the Zip file may get reopened.
- */
- if (pAsset == NULL && ap.type == kFileTypeRegular) {
- String8 path;
-
- path.appendPath((locale != NULL) ? locale : kDefaultLocale);
- path.appendPath((vendor != NULL) ? vendor : kDefaultVendor);
- path.appendPath(fileName);
-
- /* check the appropriate Zip file */
- ZipFileRO* pZip = getZipFileLocked(ap);
- if (pZip != NULL) {
- //printf("GOT zip, checking '%s'\n", (const char*) path);
- ZipEntryRO entry = pZip->findEntryByName(path.string());
- if (entry != NULL) {
- //printf("FOUND in Zip file for %s/%s-%s\n",
- // appName, locale, vendor);
- pAsset = openAssetFromZipLocked(pZip, entry, mode, path);
- pZip->releaseEntry(entry);
- }
- }
-
- if (pAsset != NULL) {
- /* create a "source" name, for debug/display */
- pAsset->setAssetSource(createZipSourceNameLocked(ZipSet::getPathName(ap.path.string()),
- String8(""), String8(fileName)));
- }
- }
-
- return pAsset;
-}
-
-/*
* Create a "source name" for a file from a Zip archive.
*/
String8 AssetManager::createZipSourceNameLocked(const String8& zipFileName,
@@ -1002,18 +774,6 @@
}
/*
- * Create a path to a loose asset (asset-base/app/locale/vendor).
- */
-String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* locale,
- const char* vendor)
-{
- String8 path(ap.path);
- path.appendPath((locale != NULL) ? locale : kDefaultLocale);
- path.appendPath((vendor != NULL) ? vendor : kDefaultVendor);
- return path;
-}
-
-/*
* Create a path to a loose asset (asset-base/app/rootDir).
*/
String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* rootDir)
@@ -1026,15 +786,6 @@
/*
* Return a pointer to one of our open Zip archives. Returns NULL if no
* matching Zip file exists.
- *
- * Right now we have 2 possible Zip files (1 each in app/"common").
- *
- * If caching is set to CACHE_OFF, to get the expected behavior we
- * need to reopen the Zip file on every request. That would be silly
- * and expensive, so instead we just check the file modification date.
- *
- * Pass in NULL values for "appName", "locale", and "vendor" if the
- * generics should be used.
*/
ZipFileRO* AssetManager::getZipFileLocked(const asset_path& ap)
{
@@ -1119,14 +870,10 @@
return pAsset;
}
-
-
/*
* Open a directory in the asset namespace.
*
- * An "asset directory" is simply the combination of all files in all
- * locations, with ".gz" stripped for loose files. With app, locale, and
- * vendor defined, we have 8 directories and 2 Zip archives to scan.
+ * An "asset directory" is simply the combination of all asset paths' "assets/" directories.
*
* Pass in "" for the root dir.
*/
@@ -1142,9 +889,6 @@
//printf("+++ openDir(%s) in '%s'\n", dirName, (const char*) mAssetBase);
- if (mCacheMode != CACHE_OFF && !mCacheValid)
- loadFileNameCacheLocked();
-
pDir = new AssetDir;
/*
@@ -1187,9 +931,7 @@
/*
* Open a directory in the non-asset namespace.
*
- * An "asset directory" is simply the combination of all files in all
- * locations, with ".gz" stripped for loose files. With app, locale, and
- * vendor defined, we have 8 directories and 2 Zip archives to scan.
+ * An "asset directory" is simply the combination of all asset paths' "assets/" directories.
*
* Pass in "" for the root dir.
*/
@@ -1205,9 +947,6 @@
//printf("+++ openDir(%s) in '%s'\n", dirName, (const char*) mAssetBase);
- if (mCacheMode != CACHE_OFF && !mCacheValid)
- loadFileNameCacheLocked();
-
pDir = new AssetDir;
pMergedInfo = new SortedVector<AssetDir::FileInfo>;
@@ -1248,74 +987,17 @@
bool AssetManager::scanAndMergeDirLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo,
const asset_path& ap, const char* rootDir, const char* dirName)
{
- SortedVector<AssetDir::FileInfo>* pContents;
- String8 path;
-
assert(pMergedInfo != NULL);
- //printf("scanAndMergeDir: %s %s %s %s\n", appName, locale, vendor,dirName);
+ //printf("scanAndMergeDir: %s %s %s\n", ap.path.string(), rootDir, dirName);
- if (mCacheValid) {
- int i, start, count;
+ String8 path = createPathNameLocked(ap, rootDir);
+ if (dirName[0] != '\0')
+ path.appendPath(dirName);
- pContents = new SortedVector<AssetDir::FileInfo>;
-
- /*
- * Get the basic partial path and find it in the cache. That's
- * the start point for the search.
- */
- path = createPathNameLocked(ap, rootDir);
- if (dirName[0] != '\0')
- path.appendPath(dirName);
-
- start = mCache.indexOf(path);
- if (start == NAME_NOT_FOUND) {
- //printf("+++ not found in cache: dir '%s'\n", (const char*) path);
- delete pContents;
- return false;
- }
-
- /*
- * The match string looks like "common/default/default/foo/bar/".
- * The '/' on the end ensures that we don't match on the directory
- * itself or on ".../foo/barfy/".
- */
- path.append("/");
-
- count = mCache.size();
-
- /*
- * Pick out the stuff in the current dir by examining the pathname.
- * It needs to match the partial pathname prefix, and not have a '/'
- * (fssep) anywhere after the prefix.
- */
- for (i = start+1; i < count; i++) {
- if (mCache[i].getFileName().length() > path.length() &&
- strncmp(mCache[i].getFileName().string(), path.string(), path.length()) == 0)
- {
- const char* name = mCache[i].getFileName().string();
- // XXX THIS IS BROKEN! Looks like we need to store the full
- // path prefix separately from the file path.
- if (strchr(name + path.length(), '/') == NULL) {
- /* grab it, reducing path to just the filename component */
- AssetDir::FileInfo tmp = mCache[i];
- tmp.setFileName(tmp.getFileName().getPathLeaf());
- pContents->add(tmp);
- }
- } else {
- /* no longer in the dir or its subdirs */
- break;
- }
-
- }
- } else {
- path = createPathNameLocked(ap, rootDir);
- if (dirName[0] != '\0')
- path.appendPath(dirName);
- pContents = scanDirLocked(path);
- if (pContents == NULL)
- return false;
- }
+ SortedVector<AssetDir::FileInfo>* pContents = scanDirLocked(path);
+ if (pContents == NULL)
+ return false;
// if we wanted to do an incremental cache fill, we would do it here
@@ -1642,153 +1324,6 @@
#endif
}
-
-/*
- * Load all files into the file name cache. We want to do this across
- * all combinations of { appname, locale, vendor }, performing a recursive
- * directory traversal.
- *
- * This is not the most efficient data structure. Also, gathering the
- * information as we needed it (file-by-file or directory-by-directory)
- * would be faster. However, on the actual device, 99% of the files will
- * live in Zip archives, so this list will be very small. The trouble
- * is that we have to check the "loose" files first, so it's important
- * that we don't beat the filesystem silly looking for files that aren't
- * there.
- *
- * Note on thread safety: this is the only function that causes updates
- * to mCache, and anybody who tries to use it will call here if !mCacheValid,
- * so we need to employ a mutex here.
- */
-void AssetManager::loadFileNameCacheLocked(void)
-{
- assert(!mCacheValid);
- assert(mCache.size() == 0);
-
-#ifdef DO_TIMINGS // need to link against -lrt for this now
- DurationTimer timer;
- timer.start();
-#endif
-
- fncScanLocked(&mCache, "");
-
-#ifdef DO_TIMINGS
- timer.stop();
- ALOGD("Cache scan took %.3fms\n",
- timer.durationUsecs() / 1000.0);
-#endif
-
-#if 0
- int i;
- printf("CACHED FILE LIST (%d entries):\n", mCache.size());
- for (i = 0; i < (int) mCache.size(); i++) {
- printf(" %d: (%d) '%s'\n", i,
- mCache.itemAt(i).getFileType(),
- (const char*) mCache.itemAt(i).getFileName());
- }
-#endif
-
- mCacheValid = true;
-}
-
-/*
- * Scan up to 8 versions of the specified directory.
- */
-void AssetManager::fncScanLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo,
- const char* dirName)
-{
- size_t i = mAssetPaths.size();
- while (i > 0) {
- i--;
- const asset_path& ap = mAssetPaths.itemAt(i);
- fncScanAndMergeDirLocked(pMergedInfo, ap, NULL, NULL, dirName);
- if (mLocale != NULL)
- fncScanAndMergeDirLocked(pMergedInfo, ap, mLocale, NULL, dirName);
- if (mVendor != NULL)
- fncScanAndMergeDirLocked(pMergedInfo, ap, NULL, mVendor, dirName);
- if (mLocale != NULL && mVendor != NULL)
- fncScanAndMergeDirLocked(pMergedInfo, ap, mLocale, mVendor, dirName);
- }
-}
-
-/*
- * Recursively scan this directory and all subdirs.
- *
- * This is similar to scanAndMergeDir, but we don't remove the .EXCLUDE
- * files, and we prepend the extended partial path to the filenames.
- */
-bool AssetManager::fncScanAndMergeDirLocked(
- SortedVector<AssetDir::FileInfo>* pMergedInfo,
- const asset_path& ap, const char* locale, const char* vendor,
- const char* dirName)
-{
- SortedVector<AssetDir::FileInfo>* pContents;
- String8 partialPath;
- String8 fullPath;
-
- // XXX This is broken -- the filename cache needs to hold the base
- // asset path separately from its filename.
-
- partialPath = createPathNameLocked(ap, locale, vendor);
- if (dirName[0] != '\0') {
- partialPath.appendPath(dirName);
- }
-
- fullPath = partialPath;
- pContents = scanDirLocked(fullPath);
- if (pContents == NULL) {
- return false; // directory did not exist
- }
-
- /*
- * Scan all subdirectories of the current dir, merging what we find
- * into "pMergedInfo".
- */
- for (int i = 0; i < (int) pContents->size(); i++) {
- if (pContents->itemAt(i).getFileType() == kFileTypeDirectory) {
- String8 subdir(dirName);
- subdir.appendPath(pContents->itemAt(i).getFileName());
-
- fncScanAndMergeDirLocked(pMergedInfo, ap, locale, vendor, subdir.string());
- }
- }
-
- /*
- * To be consistent, we want entries for the root directory. If
- * we're the root, add one now.
- */
- if (dirName[0] == '\0') {
- AssetDir::FileInfo tmpInfo;
-
- tmpInfo.set(String8(""), kFileTypeDirectory);
- tmpInfo.setSourceName(createPathNameLocked(ap, locale, vendor));
- pContents->add(tmpInfo);
- }
-
- /*
- * We want to prepend the extended partial path to every entry in
- * "pContents". It's the same value for each entry, so this will
- * not change the sorting order of the vector contents.
- */
- for (int i = 0; i < (int) pContents->size(); i++) {
- const AssetDir::FileInfo& info = pContents->itemAt(i);
- pContents->editItemAt(i).setFileName(partialPath.appendPathCopy(info.getFileName()));
- }
-
- mergeInfoLocked(pMergedInfo, pContents);
- delete pContents;
- return true;
-}
-
-/*
- * Trash the cache.
- */
-void AssetManager::purgeFileNameCacheLocked(void)
-{
- mCacheValid = false;
- mCache.clear();
-}
-
/*
* ===========================================================================
* AssetManager::SharedZip
@@ -1838,6 +1373,7 @@
Asset* AssetManager::SharedZip::getResourceTableAsset()
{
+ AutoMutex _l(gLock);
ALOGV("Getting from SharedZip %p resource asset %p\n", this, mResourceTableAsset);
return mResourceTableAsset;
}
@@ -1847,10 +1383,10 @@
{
AutoMutex _l(gLock);
if (mResourceTableAsset == NULL) {
- mResourceTableAsset = asset;
// This is not thread safe the first time it is called, so
// do it here with the global lock held.
asset->getBuffer(true);
+ mResourceTableAsset = asset;
return asset;
}
}
@@ -1921,13 +1457,6 @@
*/
/*
- * Constructor.
- */
-AssetManager::ZipSet::ZipSet(void)
-{
-}
-
-/*
* Destructor. Close any open archives.
*/
AssetManager::ZipSet::~ZipSet(void)
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index dd20a76..8c36ab5 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -229,19 +229,6 @@
}
}
-static OffscreenBuffer* createLayer(RenderState& renderState, uint32_t width, uint32_t height) {
- return renderState.layerPool().get(renderState, width, height);
-}
-
-static void destroyLayer(OffscreenBuffer* layer) {
- RenderState& renderState = layer->renderState;
- renderState.layerPool().putOrDelete(layer);
-}
-
-static bool layerMatchesWidthAndHeight(OffscreenBuffer* layer, int width, int height) {
- return layer->viewportWidth == (uint32_t) width && layer->viewportHeight == (uint32_t)height;
-}
-
void RenderNode::pushLayerUpdate(TreeInfo& info) {
LayerType layerType = properties().effectiveLayerType();
// If we are not a layer OR we cannot be rendered (eg, view was detached)
@@ -251,34 +238,15 @@
|| CC_UNLIKELY(properties().getWidth() == 0)
|| CC_UNLIKELY(properties().getHeight() == 0)) {
if (CC_UNLIKELY(mLayer)) {
- destroyLayer(mLayer);
- mLayer = nullptr;
+ renderthread::CanvasContext::destroyLayer(this);
}
return;
}
- bool transformUpdateNeeded = false;
- if (!mLayer) {
- mLayer = createLayer(info.canvasContext.getRenderState(), getWidth(), getHeight());
+ if(info.canvasContext.createOrUpdateLayer(this, *info.damageAccumulator)) {
damageSelf(info);
- transformUpdateNeeded = true;
- } else if (!layerMatchesWidthAndHeight(mLayer, getWidth(), getHeight())) {
- // TODO: remove now irrelevant, currently enqueued damage (respecting damage ordering)
- // Or, ideally, maintain damage between frames on node/layer so ordering is always correct
- RenderState& renderState = mLayer->renderState;
- if (properties().fitsOnLayer()) {
- mLayer = renderState.layerPool().resize(mLayer, getWidth(), getHeight());
- } else {
- destroyLayer(mLayer);
- mLayer = nullptr;
- }
- damageSelf(info);
- transformUpdateNeeded = true;
}
- SkRect dirty;
- info.damageAccumulator->peekAtDirty(&dirty);
-
if (!mLayer) {
Caches::getInstance().dumpMemoryUsage();
if (info.errorHandler) {
@@ -296,13 +264,8 @@
return;
}
- if (transformUpdateNeeded && mLayer) {
- // update the transform in window of the layer to reset its origin wrt light source position
- Matrix4 windowTransform;
- info.damageAccumulator->computeCurrentTransform(&windowTransform);
- mLayer->setWindowTransform(windowTransform);
- }
-
+ SkRect dirty;
+ info.damageAccumulator->peekAtDirty(&dirty);
info.layerUpdateQueue->enqueueLayerWithDamage(this, dirty);
// There might be prefetched layers that need to be accounted for.
@@ -451,8 +414,7 @@
void RenderNode::destroyHardwareResources(TreeObserver* observer, TreeInfo* info) {
if (mLayer) {
- destroyLayer(mLayer);
- mLayer = nullptr;
+ renderthread::CanvasContext::destroyLayer(this);
}
if (mDisplayList) {
for (auto&& child : mDisplayList->getChildren()) {
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index a0679b1..da93c13 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -199,6 +199,7 @@
}
OffscreenBuffer* getLayer() const { return mLayer; }
OffscreenBuffer** getLayerHandle() { return &mLayer; } // ugh...
+ void setLayer(OffscreenBuffer* layer) { mLayer = layer; }
// Note: The position callbacks are relying on the listener using
// the frameNumber to appropriately batch/synchronize these transactions.
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 43471e5..0f2d55b 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -85,6 +85,18 @@
return nullptr;
}
+void CanvasContext::destroyLayer(RenderNode* node) {
+ auto renderType = Properties::getRenderPipelineType();
+ switch (renderType) {
+ case RenderPipelineType::OpenGL:
+ OpenGLPipeline::destroyLayer(node);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType);
+ break;
+ }
+}
+
CanvasContext::CanvasContext(RenderThread& thread, bool translucent,
RenderNode* rootRenderNode, IContextFactory* contextFactory,
std::unique_ptr<IRenderPipeline> renderPipeline)
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 7ebe0ae..652cddd 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -68,6 +68,23 @@
RenderNode* rootRenderNode, IContextFactory* contextFactory);
virtual ~CanvasContext();
+ /**
+ * Update or create a layer specific for the provided RenderNode. The layer
+ * attached to the node will be specific to the RenderPipeline used by this
+ * context
+ *
+ * @return true if the layer has been created or updated
+ */
+ bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& dmgAccumulator) {
+ return mRenderPipeline->createOrUpdateLayer(node, dmgAccumulator);
+ }
+
+ /**
+ * Destroy any layers that have been attached to the provided RenderNode removing
+ * any state that may have been set during createOrUpdateLayer().
+ */
+ static void destroyLayer(RenderNode* node);
+
// Won't take effect until next EGLSurface creation
void setSwapBehavior(SwapBehavior swapBehavior);
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index 3250fed..97cdf7f 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -67,6 +67,8 @@
LayerUpdateQueue* layerUpdateQueue, bool opaque,
const BakedOpRenderer::LightInfo& lightInfo) = 0;
virtual TaskManager* getTaskManager() = 0;
+ virtual bool createOrUpdateLayer(RenderNode* node,
+ const DamageAccumulator& damageAccumulator) = 0;
virtual ~IRenderPipeline() {}
};
diff --git a/libs/hwui/renderthread/OpenGLPipeline.cpp b/libs/hwui/renderthread/OpenGLPipeline.cpp
index c8971f8..c758f6c 100644
--- a/libs/hwui/renderthread/OpenGLPipeline.cpp
+++ b/libs/hwui/renderthread/OpenGLPipeline.cpp
@@ -183,6 +183,46 @@
return &Caches::getInstance().tasks;
}
+static bool layerMatchesWH(OffscreenBuffer* layer, int width, int height) {
+ return layer->viewportWidth == (uint32_t)width && layer->viewportHeight == (uint32_t)height;
+}
+
+bool OpenGLPipeline::createOrUpdateLayer(RenderNode* node,
+ const DamageAccumulator& damageAccumulator) {
+ RenderState& renderState = mRenderThread.renderState();
+ OffscreenBufferPool& layerPool = renderState.layerPool();
+ bool transformUpdateNeeded = false;
+ if (node->getLayer() == nullptr) {
+ node->setLayer(layerPool.get(renderState, node->getWidth(), node->getHeight()));
+ transformUpdateNeeded = true;
+ } else if (!layerMatchesWH(node->getLayer(), node->getWidth(), node->getHeight())) {
+ // TODO: remove now irrelevant, currently enqueued damage (respecting damage ordering)
+ // Or, ideally, maintain damage between frames on node/layer so ordering is always correct
+ if (node->properties().fitsOnLayer()) {
+ node->setLayer(layerPool.resize(node->getLayer(), node->getWidth(), node->getHeight()));
+ } else {
+ destroyLayer(node);
+ }
+ transformUpdateNeeded = true;
+ }
+
+ if (transformUpdateNeeded && node->getLayer()) {
+ // update the transform in window of the layer to reset its origin wrt light source position
+ Matrix4 windowTransform;
+ damageAccumulator.computeCurrentTransform(&windowTransform);
+ node->getLayer()->setWindowTransform(windowTransform);
+ }
+
+ return transformUpdateNeeded;
+}
+
+void OpenGLPipeline::destroyLayer(RenderNode* node) {
+ if (OffscreenBuffer* layer = node->getLayer()) {
+ layer->renderState.layerPool().putOrDelete(layer);
+ node->setLayer(nullptr);
+ }
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/OpenGLPipeline.h b/libs/hwui/renderthread/OpenGLPipeline.h
index e08fd9b..34d9bc0 100644
--- a/libs/hwui/renderthread/OpenGLPipeline.h
+++ b/libs/hwui/renderthread/OpenGLPipeline.h
@@ -56,6 +56,9 @@
LayerUpdateQueue* layerUpdateQueue, bool opaque,
const BakedOpRenderer::LightInfo& lightInfo) override;
TaskManager* getTaskManager() override;
+ bool createOrUpdateLayer(RenderNode* node,
+ const DamageAccumulator& damageAccumulator) override;
+ static void destroyLayer(RenderNode* node);
private:
EglManager& mEglManager;
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index abbace1..bd68fec 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -389,8 +389,8 @@
/** @hide Stream over a socket, limited to a single stream */
public static final int OUTPUT_FORMAT_RTP_AVP = 7;
- /** @hide H.264/AAC data encapsulated in MPEG2/TS */
- public static final int OUTPUT_FORMAT_MPEG2TS = 8;
+ /** H.264/AAC data encapsulated in MPEG2/TS */
+ public static final int MPEG_2_TS = 8;
/** VP8/VORBIS data in a WEBM container */
public static final int WEBM = 9;
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 88b566a..5f0d3df 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -31,7 +31,7 @@
struct AMessage;
struct AString;
struct ICrypto;
-struct IGraphicBufferProducer;
+class IGraphicBufferProducer;
struct MediaCodec;
struct PersistentSurface;
class Surface;
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index a9b7062..401012f 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -352,7 +352,7 @@
effectCallback,
&lpJniStorage->mCallbackData,
(audio_session_t) sessionId,
- 0);
+ AUDIO_IO_HANDLE_NONE);
if (lpAudioEffect == 0) {
ALOGE("Error creating AudioEffect");
goto setup_failure;
diff --git a/media/tests/MediaFrameworkTest/Android.mk b/media/tests/MediaFrameworkTest/Android.mk
index 20575e0..308c665 100644
--- a/media/tests/MediaFrameworkTest/Android.mk
+++ b/media/tests/MediaFrameworkTest/Android.mk
@@ -7,8 +7,6 @@
LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_JAVA_LANGUAGE_VERSION := 1.8
-
LOCAL_STATIC_JAVA_LIBRARIES := \
mockito-target-minus-junit4 \
android-support-test \
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 33d6b9a..3b575a8 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -41,6 +41,7 @@
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
+import android.provider.DocumentsContract.Path;
import android.provider.DocumentsProvider;
import android.provider.MediaStore;
import android.provider.Settings;
@@ -48,6 +49,7 @@
import android.util.ArrayMap;
import android.util.DebugUtils;
import android.util.Log;
+import android.util.Pair;
import android.webkit.MimeTypeMap;
import com.android.internal.annotations.GuardedBy;
@@ -183,7 +185,8 @@
root.rootId = rootId;
root.volumeId = volume.id;
root.flags = Root.FLAG_LOCAL_ONLY
- | Root.FLAG_SUPPORTS_SEARCH | Root.FLAG_SUPPORTS_IS_CHILD;
+ | Root.FLAG_SUPPORTS_SEARCH
+ | Root.FLAG_SUPPORTS_IS_CHILD;
final DiskInfo disk = volume.getDisk();
if (DEBUG) Log.d(TAG, "Disk for root " + rootId + " is " + disk);
@@ -270,7 +273,6 @@
return projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION;
}
-
private String getDocIdForFile(File file) throws FileNotFoundException {
return getDocIdForFileMaybeCreate(file, false);
}
@@ -323,6 +325,11 @@
}
private File getFileForDocId(String docId, boolean visible) throws FileNotFoundException {
+ return resolveDocId(docId, visible).second;
+ }
+
+ private Pair<RootInfo, File> resolveDocId(String docId, boolean visible)
+ throws FileNotFoundException {
final int splitIndex = docId.indexOf(':', 1);
final String tag = docId.substring(0, splitIndex);
final String path = docId.substring(splitIndex + 1);
@@ -346,7 +353,7 @@
if (!target.exists()) {
throw new FileNotFoundException("Missing file for " + docId + " at " + target);
}
- return target;
+ return Pair.create(root, target);
}
private void includeFile(MatrixCursor result, String docId, File file)
@@ -423,6 +430,28 @@
}
@Override
+ public Path findPath(String documentId)
+ throws FileNotFoundException {
+ LinkedList<String> path = new LinkedList<>();
+
+ final Pair<RootInfo, File> resolvedDocId = resolveDocId(documentId, false);
+ RootInfo root = resolvedDocId.first;
+ File file = resolvedDocId.second;
+
+ if (!file.exists()) {
+ throw new FileNotFoundException();
+ }
+
+ while (file != null && file.getAbsolutePath().startsWith(root.path.getAbsolutePath())) {
+ path.addFirst(getDocIdForFile(file));
+
+ file = file.getParentFile();
+ }
+
+ return new Path(root.rootId, path);
+ }
+
+ @Override
public String createDocument(String docId, String mimeType, String displayName)
throws FileNotFoundException {
displayName = FileUtils.buildValidFatFilename(displayName);
diff --git a/packages/Keyguard/res/values-da/strings.xml b/packages/Keyguard/res/values-da/strings.xml
index ebea6ee..b98a253 100644
--- a/packages/Keyguard/res/values-da/strings.xml
+++ b/packages/Keyguard/res/values-da/strings.xml
@@ -128,5 +128,5 @@
<item quantity="one">Enheden blev sidst låst op for <xliff:g id="NUMBER_1">%d</xliff:g> timer siden. Bekræft adgangskoden.</item>
<item quantity="other">Enheden blev sidst låst op for <xliff:g id="NUMBER_1">%d</xliff:g> timer siden. Bekræft adgangskoden.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Kan ikke genkendes"</string>
+ <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Ikke genkendt"</string>
</resources>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index b3cfea5..23c6615 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -46,7 +46,6 @@
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.os.UserManager;
import android.print.IPrintDocumentAdapter;
import android.print.PageRange;
@@ -760,8 +759,11 @@
mPrintJob.setPrinterId(printerInfo.getId());
mPrintJob.setPrinterName(printerInfo.getName());
- if (printerInfo.getCapabilities() != null) {
+ if (canPrint(printerInfo)) {
updatePrintAttributesFromCapabilities(printerInfo.getCapabilities());
+ onPrinterAvailable(printerInfo);
+ } else {
+ onPrinterUnavailable(printerInfo);
}
mDestinationSpinnerAdapter.ensurePrinterInVisibleAdapterPosition(printerInfo);
@@ -2050,7 +2052,7 @@
}
public void onPrinterUnavailable(PrinterInfo printer) {
- if (mCurrentPrinter.getId().equals(printer.getId())) {
+ if (mCurrentPrinter == null || mCurrentPrinter.getId().equals(printer.getId())) {
setState(STATE_PRINTER_UNAVAILABLE);
mPrintedDocument.cancel(false);
ensureErrorUiShown(getString(R.string.print_error_printer_unavailable),
@@ -2309,8 +2311,7 @@
public int getPrinterIndex(PrinterId printerId) {
for (int i = 0; i < getCount(); i++) {
PrinterHolder printerHolder = (PrinterHolder) getItem(i);
- if (printerHolder != null && !printerHolder.removed
- && printerHolder.printer.getId().equals(printerId)) {
+ if (printerHolder != null && printerHolder.printer.getId().equals(printerId)) {
return i;
}
}
@@ -2539,7 +2540,11 @@
if (updatedPrinter != null) {
printerHolder.printer = updatedPrinter;
printerHolder.removed = false;
- onPrinterAvailable(printerHolder.printer);
+ if (canPrint(printerHolder.printer)) {
+ onPrinterAvailable(printerHolder.printer);
+ } else {
+ onPrinterUnavailable(printerHolder.printer);
+ }
newPrinterHolders.add(printerHolder);
} else if (mCurrentPrinter != null && mCurrentPrinter.getId().equals(oldPrinterId)){
printerHolder.removed = true;
diff --git a/packages/PrintSpooler/tests/Android.mk b/packages/PrintSpooler/tests/Android.mk
new file mode 100644
index 0000000..83e00ce
--- /dev/null
+++ b/packages/PrintSpooler/tests/Android.mk
@@ -0,0 +1,19 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/packages/PrintSpooler/tests/outofprocess/Android.mk b/packages/PrintSpooler/tests/outofprocess/Android.mk
new file mode 100644
index 0000000..d1d0ee4
--- /dev/null
+++ b/packages/PrintSpooler/tests/outofprocess/Android.mk
@@ -0,0 +1,28 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ub-uiautomator mockito-target-minus-junit4
+
+LOCAL_PACKAGE_NAME := PrintSpoolerOutOfProcessTests
+
+include $(BUILD_PACKAGE)
diff --git a/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml b/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml
new file mode 100644
index 0000000..4a05f6f
--- /dev/null
+++ b/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.printspooler.outofprocess.tests">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+
+ <activity android:name=".PrintTestActivity"/>
+
+ <service
+ android:name=".mockservice.MockPrintService"
+ android:permission="android.permission.BIND_PRINT_SERVICE">
+
+ <intent-filter>
+ <action android:name="android.printservice.PrintService" />
+ </intent-filter>
+ <meta-data
+ android:name="android.printservice"
+ android:resource="@xml/printservice">
+ </meta-data>
+ </service>
+
+ <activity
+ android:name=".mockservice.SettingsActivity"
+ android:permission="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY"
+ android:exported="true">
+ </activity>
+
+ <activity
+ android:name=".mockservice.AddPrintersActivity"
+ android:exported="true">
+ </activity>
+
+ </application>
+
+ <!-- This runs in its own process, hence it instruments itself -->
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.printspooler.outofprocess.tests"
+ android:label="PrintSpooler Out of Process Test Cases">
+ </instrumentation>
+
+</manifest>
diff --git a/packages/PrintSpooler/tests/outofprocess/res/xml/printservice.xml b/packages/PrintSpooler/tests/outofprocess/res/xml/printservice.xml
new file mode 100644
index 0000000..9eecf45
--- /dev/null
+++ b/packages/PrintSpooler/tests/outofprocess/res/xml/printservice.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Copyright (C) 2016 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.
+ -->
+
+<print-service xmlns:android="http://schemas.android.com/apk/res/android"
+ android:addPrintersActivity="com.android.printspooler.outofprocess.tests.mockservice.AddPrintersActivity" />
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/BasePrintTest.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/BasePrintTest.java
new file mode 100644
index 0000000..4de3570
--- /dev/null
+++ b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/BasePrintTest.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2016 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.printspooler.outofprocess.tests;
+
+import android.annotation.NonNull;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.print.PrintAttributes;
+import android.print.PrintDocumentAdapter;
+import android.print.PrintManager;
+import android.print.PrinterId;
+import com.android.printspooler.outofprocess.tests.mockservice.PrintServiceCallbacks;
+import com.android.printspooler.outofprocess.tests.mockservice.PrinterDiscoverySessionCallbacks;
+import com.android.printspooler.outofprocess.tests.mockservice.StubbablePrinterDiscoverySession;
+import android.printservice.CustomPrinterIconCallback;
+import android.printservice.PrintJob;
+import android.printservice.PrintService;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.uiautomator.UiDevice;
+import org.junit.After;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.mockito.stubbing.Answer;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.List;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * This is the base class for print tests.
+ */
+abstract class BasePrintTest {
+ protected static final long OPERATION_TIMEOUT = 30000;
+ private static final String PM_CLEAR_SUCCESS_OUTPUT = "Success";
+ private static final int CURRENT_USER_ID = -2; // Mirrors UserHandle.USER_CURRENT
+
+ private android.print.PrintJob mPrintJob;
+
+ private static Instrumentation sInstrumentation;
+ private static UiDevice sUiDevice;
+
+ @Rule
+ public ActivityTestRule<PrintTestActivity> mActivityRule =
+ new ActivityTestRule<>(PrintTestActivity.class, false, true);
+
+ /**
+ * Return the UI device
+ *
+ * @return the UI device
+ */
+ public UiDevice getUiDevice() {
+ return sUiDevice;
+ }
+
+ protected static Instrumentation getInstrumentation() {
+ return sInstrumentation;
+ }
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ sInstrumentation = InstrumentationRegistry.getInstrumentation();
+ assumeTrue(sInstrumentation.getContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_PRINTING));
+
+ sUiDevice = UiDevice.getInstance(sInstrumentation);
+
+ // Make sure we start with a clean slate.
+ clearPrintSpoolerData();
+
+ // Workaround for dexmaker bug: https://code.google.com/p/dexmaker/issues/detail?id=2
+ // Dexmaker is used by mockito.
+ System.setProperty("dexmaker.dexcache", getInstrumentation()
+ .getTargetContext().getCacheDir().getPath());
+ }
+
+ @After
+ public void exitActivities() throws Exception {
+ // Exit print spooler
+ getUiDevice().pressBack();
+ getUiDevice().pressBack();
+ }
+
+ protected android.print.PrintJob print(@NonNull final PrintDocumentAdapter adapter,
+ final PrintAttributes attributes) {
+ // Initiate printing as if coming from the app.
+ getInstrumentation().runOnMainSync(() -> {
+ PrintManager printManager = (PrintManager) getActivity()
+ .getSystemService(Context.PRINT_SERVICE);
+ mPrintJob = printManager.print("Print job", adapter, attributes);
+ });
+
+ return mPrintJob;
+ }
+
+ protected PrintTestActivity getActivity() {
+ return mActivityRule.getActivity();
+ }
+
+ public static String runShellCommand(Instrumentation instrumentation, String cmd)
+ throws IOException {
+ ParcelFileDescriptor pfd = instrumentation.getUiAutomation().executeShellCommand(cmd);
+ byte[] buf = new byte[512];
+ int bytesRead;
+ FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+ StringBuilder stdout = new StringBuilder();
+ while ((bytesRead = fis.read(buf)) != -1) {
+ stdout.append(new String(buf, 0, bytesRead));
+ }
+ fis.close();
+ return stdout.toString();
+ }
+
+ protected static void clearPrintSpoolerData() throws Exception {
+ assertTrue("failed to clear print spooler data",
+ runShellCommand(getInstrumentation(), String.format(
+ "pm clear --user %d %s", CURRENT_USER_ID,
+ PrintManager.PRINT_SPOOLER_PACKAGE_NAME))
+ .contains(PM_CLEAR_SUCCESS_OUTPUT));
+ }
+
+ @SuppressWarnings("unchecked")
+ protected PrinterDiscoverySessionCallbacks createMockPrinterDiscoverySessionCallbacks(
+ Answer<Void> onStartPrinterDiscovery, Answer<Void> onStopPrinterDiscovery,
+ Answer<Void> onValidatePrinters, Answer<Void> onStartPrinterStateTracking,
+ Answer<Void> onRequestCustomPrinterIcon, Answer<Void> onStopPrinterStateTracking,
+ Answer<Void> onDestroy) {
+ PrinterDiscoverySessionCallbacks callbacks = mock(PrinterDiscoverySessionCallbacks.class);
+
+ doCallRealMethod().when(callbacks).setSession(any(StubbablePrinterDiscoverySession.class));
+ when(callbacks.getSession()).thenCallRealMethod();
+
+ if (onStartPrinterDiscovery != null) {
+ doAnswer(onStartPrinterDiscovery).when(callbacks).onStartPrinterDiscovery(
+ any(List.class));
+ }
+ if (onStopPrinterDiscovery != null) {
+ doAnswer(onStopPrinterDiscovery).when(callbacks).onStopPrinterDiscovery();
+ }
+ if (onValidatePrinters != null) {
+ doAnswer(onValidatePrinters).when(callbacks).onValidatePrinters(
+ any(List.class));
+ }
+ if (onStartPrinterStateTracking != null) {
+ doAnswer(onStartPrinterStateTracking).when(callbacks).onStartPrinterStateTracking(
+ any(PrinterId.class));
+ }
+ if (onRequestCustomPrinterIcon != null) {
+ doAnswer(onRequestCustomPrinterIcon).when(callbacks).onRequestCustomPrinterIcon(
+ any(PrinterId.class), any(CancellationSignal.class),
+ any(CustomPrinterIconCallback.class));
+ }
+ if (onStopPrinterStateTracking != null) {
+ doAnswer(onStopPrinterStateTracking).when(callbacks).onStopPrinterStateTracking(
+ any(PrinterId.class));
+ }
+ if (onDestroy != null) {
+ doAnswer(onDestroy).when(callbacks).onDestroy();
+ }
+
+ return callbacks;
+ }
+
+ protected PrintServiceCallbacks createMockPrintServiceCallbacks(
+ Answer<PrinterDiscoverySessionCallbacks> onCreatePrinterDiscoverySessionCallbacks,
+ Answer<Void> onPrintJobQueued, Answer<Void> onRequestCancelPrintJob) {
+ final PrintServiceCallbacks service = mock(PrintServiceCallbacks.class);
+
+ doCallRealMethod().when(service).setService(any(PrintService.class));
+ when(service.getService()).thenCallRealMethod();
+
+ if (onCreatePrinterDiscoverySessionCallbacks != null) {
+ doAnswer(onCreatePrinterDiscoverySessionCallbacks).when(service)
+ .onCreatePrinterDiscoverySessionCallbacks();
+ }
+ if (onPrintJobQueued != null) {
+ doAnswer(onPrintJobQueued).when(service).onPrintJobQueued(any(PrintJob.class));
+ }
+ if (onRequestCancelPrintJob != null) {
+ doAnswer(onRequestCancelPrintJob).when(service).onRequestCancelPrintJob(
+ any(PrintJob.class));
+ }
+
+ return service;
+ }
+}
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/PrintTestActivity.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/PrintTestActivity.java
new file mode 100644
index 0000000..4905a0b
--- /dev/null
+++ b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/PrintTestActivity.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2016 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.printspooler.outofprocess.tests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public class PrintTestActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+ }
+}
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
new file mode 100644
index 0000000..184e559
--- /dev/null
+++ b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
@@ -0,0 +1,460 @@
+/*
+ * Copyright (C) 2016 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.printspooler.outofprocess.tests;
+
+import android.graphics.pdf.PdfDocument;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.print.PageRange;
+import android.print.PrintAttributes;
+import android.print.PrintDocumentAdapter;
+import android.print.PrintDocumentInfo;
+import android.print.PrinterCapabilitiesInfo;
+import android.print.PrinterId;
+import android.print.PrinterInfo;
+import com.android.printspooler.outofprocess.tests.mockservice.AddPrintersActivity;
+import com.android.printspooler.outofprocess.tests.mockservice.MockPrintService;
+import com.android.printspooler.outofprocess.tests.mockservice.PrinterDiscoverySessionCallbacks;
+import com.android.printspooler.outofprocess.tests.mockservice.StubbablePrinterDiscoverySession;
+import android.print.pdf.PrintedPdfDocument;
+import android.support.test.filters.LargeTest;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.UiSelector;
+import android.support.test.uiautomator.Until;
+import android.util.Log;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Supplier;
+
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * Tests for the basic printing workflows
+ */
+@RunWith(Parameterized.class)
+public class WorkflowTest extends BasePrintTest {
+ private static final String LOG_TAG = WorkflowTest.class.getSimpleName();
+
+ private static float sWindowAnimationScaleBefore;
+ private static float sTransitionAnimationScaleBefore;
+ private static float sAnimatiorDurationScaleBefore;
+
+ private PrintAttributes.MediaSize mFirst;
+ private boolean mSelectPrinter;
+ private PrintAttributes.MediaSize mSecond;
+
+ public WorkflowTest(PrintAttributes.MediaSize first, boolean selectPrinter,
+ PrintAttributes.MediaSize second) {
+ mFirst = first;
+ mSelectPrinter = selectPrinter;
+ mSecond = second;
+ }
+
+ interface InterruptableConsumer<T> {
+ void accept(T t) throws InterruptedException;
+ }
+
+ /**
+ * Execute {@code waiter} until {@code condition} is met.
+ *
+ * @param condition Conditions to wait for
+ * @param waiter Code to execute while waiting
+ */
+ private void waitWithTimeout(Supplier<Boolean> condition, InterruptableConsumer<Long> waiter)
+ throws TimeoutException, InterruptedException {
+ long startTime = System.currentTimeMillis();
+ while (condition.get()) {
+ long timeLeft = OPERATION_TIMEOUT - (System.currentTimeMillis() - startTime);
+ if (timeLeft < 0) {
+ throw new TimeoutException();
+ }
+
+ waiter.accept(timeLeft);
+ }
+ }
+
+ /**
+ * Executes a shell command using shell user identity, and return the standard output in
+ * string.
+ *
+ * @param cmd the command to run
+ *
+ * @return the standard output of the command
+ */
+ private static String runShellCommand(String cmd) throws IOException {
+ try (FileInputStream is = new ParcelFileDescriptor.AutoCloseInputStream(
+ getInstrumentation().getUiAutomation().executeShellCommand(cmd))) {
+ byte[] buf = new byte[64];
+ int bytesRead;
+
+ StringBuilder stdout = new StringBuilder();
+ while ((bytesRead = is.read(buf)) != -1) {
+ stdout.append(new String(buf, 0, bytesRead));
+ }
+
+ return stdout.toString();
+ }
+ }
+
+ @BeforeClass
+ public static void disableAnimations() throws Exception {
+ try {
+ sWindowAnimationScaleBefore = Float.parseFloat(runShellCommand(
+ "settings get global window_animation_scale"));
+
+ runShellCommand("settings put global window_animation_scale 0");
+ } catch (NumberFormatException e) {
+ sWindowAnimationScaleBefore = Float.NaN;
+ }
+ try {
+ sTransitionAnimationScaleBefore = Float.parseFloat(runShellCommand(
+ "settings get global transition_animation_scale"));
+
+ runShellCommand("settings put global transition_animation_scale 0");
+ } catch (NumberFormatException e) {
+ sTransitionAnimationScaleBefore = Float.NaN;
+ }
+ try {
+ sAnimatiorDurationScaleBefore = Float.parseFloat(runShellCommand(
+ "settings get global animator_duration_scale"));
+
+ runShellCommand("settings put global animator_duration_scale 0");
+ } catch (NumberFormatException e) {
+ sAnimatiorDurationScaleBefore = Float.NaN;
+ }
+ }
+
+ @AfterClass
+ public static void enableAnimations() throws Exception {
+ if (sWindowAnimationScaleBefore != Float.NaN) {
+ runShellCommand(
+ "settings put global window_animation_scale " + sWindowAnimationScaleBefore);
+ }
+ if (sTransitionAnimationScaleBefore != Float.NaN) {
+ runShellCommand(
+ "settings put global transition_animation_scale " +
+ sTransitionAnimationScaleBefore);
+ }
+ if (sAnimatiorDurationScaleBefore != Float.NaN) {
+ runShellCommand(
+ "settings put global animator_duration_scale " + sAnimatiorDurationScaleBefore);
+ }
+ }
+
+ /** Add a printer with a given name and supported mediasize to a session */
+ private void addPrinter(StubbablePrinterDiscoverySession session,
+ String name, PrintAttributes.MediaSize mediaSize) {
+ PrinterId printerId = session.getService().generatePrinterId(name);
+ List<PrinterInfo> printers = new ArrayList<>(1);
+
+ PrinterCapabilitiesInfo.Builder builder =
+ new PrinterCapabilitiesInfo.Builder(printerId);
+
+ PrinterInfo printerInfo;
+ if (mediaSize != null) {
+ builder.setMinMargins(new PrintAttributes.Margins(0, 0, 0, 0))
+ .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
+ PrintAttributes.COLOR_MODE_COLOR)
+ .addMediaSize(mediaSize, true)
+ .addResolution(new PrintAttributes.Resolution("300x300", "300x300", 300, 300),
+ true);
+
+ printerInfo = new PrinterInfo.Builder(printerId, name,
+ PrinterInfo.STATUS_IDLE).setCapabilities(builder.build()).build();
+ } else {
+ printerInfo = (new PrinterInfo.Builder(printerId, name,
+ PrinterInfo.STATUS_IDLE)).build();
+ }
+
+ printers.add(printerInfo);
+ session.addPrinters(printers);
+ }
+
+ /** Find a certain element in the UI and click on it */
+ private void clickOn(UiSelector selector) throws UiObjectNotFoundException {
+ Log.i(LOG_TAG, "Click on " + selector);
+ UiObject view = getUiDevice().findObject(selector);
+ view.click();
+ getUiDevice().waitForIdle();
+ }
+
+ /** Find a certain text in the UI and click on it */
+ private void clickOnText(String text) throws UiObjectNotFoundException {
+ clickOn(new UiSelector().text(text));
+ }
+
+ /** Set the printer in the print activity */
+ private void setPrinter(String printerName) throws UiObjectNotFoundException {
+ clickOn(new UiSelector().resourceId("com.android.printspooler:id/destination_spinner"));
+
+ clickOnText(printerName);
+ }
+
+ /**
+ * Init mock print servic that returns a single printer by default.
+ *
+ * @param sessionRef Where to store the reference to the session once started
+ */
+ private void setMockPrintServiceCallbacks(StubbablePrinterDiscoverySession[] sessionRef,
+ ArrayList<String> trackedPrinters, PrintAttributes.MediaSize mediaSize) {
+ MockPrintService.setCallbacks(createMockPrintServiceCallbacks(
+ inv -> createMockPrinterDiscoverySessionCallbacks(inv2 -> {
+ synchronized (sessionRef) {
+ sessionRef[0] = ((PrinterDiscoverySessionCallbacks) inv2.getMock())
+ .getSession();
+
+ addPrinter(sessionRef[0], "1st printer", mediaSize);
+
+ sessionRef.notifyAll();
+ }
+ return null;
+ },
+ null, null, inv2 -> {
+ synchronized (trackedPrinters) {
+ trackedPrinters
+ .add(((PrinterId) inv2.getArguments()[0]).getLocalId());
+ trackedPrinters.notifyAll();
+ }
+ return null;
+ }, null, inv2 -> {
+ synchronized (trackedPrinters) {
+ trackedPrinters
+ .remove(((PrinterId) inv2.getArguments()[0]).getLocalId());
+ trackedPrinters.notifyAll();
+ }
+ return null;
+ }, inv2 -> {
+ synchronized (sessionRef) {
+ sessionRef[0] = null;
+ sessionRef.notifyAll();
+ }
+ return null;
+ }
+ ), null, null));
+ }
+
+ /**
+ * Start print operation that just prints a single empty page
+ *
+ * @param printAttributesRef Where to store the reference to the print attributes once started
+ */
+ private void print(PrintAttributes[] printAttributesRef) {
+ print(new PrintDocumentAdapter() {
+ @Override
+ public void onStart() {
+ }
+
+ @Override
+ public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
+ CancellationSignal cancellationSignal, LayoutResultCallback callback,
+ Bundle extras) {
+ callback.onLayoutFinished((new PrintDocumentInfo.Builder("doc")).build(),
+ !newAttributes.equals(printAttributesRef[0]));
+
+ synchronized (printAttributesRef) {
+ printAttributesRef[0] = newAttributes;
+ printAttributesRef.notifyAll();
+ }
+ }
+
+ @Override
+ public void onWrite(PageRange[] pages, ParcelFileDescriptor destination,
+ CancellationSignal cancellationSignal, WriteResultCallback callback) {
+ try {
+ try {
+ PrintedPdfDocument document = new PrintedPdfDocument(getActivity(),
+ printAttributesRef[0]);
+ try {
+ PdfDocument.Page page = document.startPage(0);
+ document.finishPage(page);
+ try (FileOutputStream os = new FileOutputStream(
+ destination.getFileDescriptor())) {
+ document.writeTo(os);
+ os.flush();
+ }
+ } finally {
+ document.close();
+ }
+ } finally {
+ destination.close();
+ }
+
+ callback.onWriteFinished(pages);
+ } catch (IOException e) {
+ callback.onWriteFailed(e.getMessage());
+ }
+ }
+ }, null);
+ }
+
+ @Parameterized.Parameters
+ public static Collection<Object[]> getParameters() {
+ ArrayList<Object[]> tests = new ArrayList<>();
+
+ for (PrintAttributes.MediaSize first : new PrintAttributes.MediaSize[]{
+ PrintAttributes.MediaSize.ISO_A0, null}) {
+ for (Boolean selectPrinter : new Boolean[]{true, false}) {
+ for (PrintAttributes.MediaSize second : new PrintAttributes.MediaSize[]{
+ PrintAttributes.MediaSize.ISO_A1, null}) {
+ // If we do not use the second printer, no need to try various options
+ if (!selectPrinter && second == null) {
+ continue;
+ }
+ tests.add(new Object[]{first, selectPrinter, second});
+ }
+ }
+ }
+
+ return tests;
+ }
+
+ @Test
+ @LargeTest
+ public void addAndSelectPrinter() throws Exception {
+ final StubbablePrinterDiscoverySession session[] = new StubbablePrinterDiscoverySession[1];
+ final PrintAttributes printAttributes[] = new PrintAttributes[1];
+ ArrayList<String> trackedPrinters = new ArrayList<>();
+
+ Log.i(LOG_TAG, "Running " + mFirst + " " + mSelectPrinter + " " + mSecond);
+
+ setMockPrintServiceCallbacks(session, trackedPrinters, mFirst);
+ print(printAttributes);
+
+ // We are now in the PrintActivity
+ Log.i(LOG_TAG, "Waiting for session");
+ synchronized (session) {
+ waitWithTimeout(() -> session[0] == null, session::wait);
+ }
+
+ setPrinter("1st printer");
+
+ Log.i(LOG_TAG, "Waiting for 1st printer to be tracked");
+ synchronized (trackedPrinters) {
+ waitWithTimeout(() -> !trackedPrinters.contains("1st printer"), trackedPrinters::wait);
+ }
+
+ if (mFirst != null) {
+ Log.i(LOG_TAG, "Waiting for print attributes to change");
+ synchronized (printAttributes) {
+ waitWithTimeout(
+ () -> printAttributes[0] == null ||
+ !printAttributes[0].getMediaSize().equals(
+ mFirst), printAttributes::wait);
+ }
+ } else {
+ Log.i(LOG_TAG, "Waiting for error message");
+ assertNotNull(getUiDevice().wait(Until.findObject(
+ By.text("This printer isn't available right now.")), OPERATION_TIMEOUT));
+ }
+
+ setPrinter("All printers\u2026");
+
+ // We are now in the SelectPrinterActivity
+ clickOnText("Add printer");
+
+ // We are now in the AddPrinterActivity
+ AddPrintersActivity.addObserver(
+ () -> addPrinter(session[0], "2nd printer", mSecond));
+
+ // This executes the observer registered above
+ clickOn(new UiSelector().text(MockPrintService.class.getCanonicalName())
+ .resourceId("com.android.printspooler:id/title"));
+
+ getUiDevice().pressBack();
+ AddPrintersActivity.clearObservers();
+
+ if (mSelectPrinter) {
+ // We are now in the SelectPrinterActivity
+ clickOnText("2nd printer");
+ } else {
+ getUiDevice().pressBack();
+ }
+
+ // We are now in the PrintActivity
+ if (mSelectPrinter) {
+ if (mSecond != null) {
+ Log.i(LOG_TAG, "Waiting for print attributes to change");
+ synchronized (printAttributes) {
+ waitWithTimeout(
+ () -> printAttributes[0] == null ||
+ !printAttributes[0].getMediaSize().equals(
+ mSecond), printAttributes::wait);
+ }
+ } else {
+ Log.i(LOG_TAG, "Waiting for error message");
+ assertNotNull(getUiDevice().wait(Until.findObject(
+ By.text("This printer isn't available right now.")), OPERATION_TIMEOUT));
+ }
+
+ Log.i(LOG_TAG, "Waiting for 1st printer to be not tracked");
+ synchronized (trackedPrinters) {
+ waitWithTimeout(() -> trackedPrinters.contains("1st printer"),
+ trackedPrinters::wait);
+ }
+
+ Log.i(LOG_TAG, "Waiting for 2nd printer to be tracked");
+ synchronized (trackedPrinters) {
+ waitWithTimeout(() -> !trackedPrinters.contains("2nd printer"),
+ trackedPrinters::wait);
+ }
+ } else {
+ Thread.sleep(100);
+
+ if (mFirst != null) {
+ Log.i(LOG_TAG, "Waiting for print attributes to change");
+ synchronized (printAttributes) {
+ waitWithTimeout(
+ () -> printAttributes[0] == null ||
+ !printAttributes[0].getMediaSize().equals(
+ mFirst), printAttributes::wait);
+ }
+ } else {
+ Log.i(LOG_TAG, "Waiting for error message");
+ assertNotNull(getUiDevice().wait(Until.findObject(
+ By.text("This printer isn't available right now.")), OPERATION_TIMEOUT));
+ }
+
+ Log.i(LOG_TAG, "Waiting for 1st printer to be tracked");
+ synchronized (trackedPrinters) {
+ waitWithTimeout(() -> !trackedPrinters.contains("1st printer"),
+ trackedPrinters::wait);
+ }
+ }
+
+ getUiDevice().pressBack();
+
+ // We are back in the test activity
+ Log.i(LOG_TAG, "Waiting for session to end");
+ synchronized (session) {
+ waitWithTimeout(() -> session[0] != null, session::wait);
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/print/mockservice/AddPrintersActivity.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/AddPrintersActivity.java
similarity index 95%
rename from core/tests/coretests/src/android/print/mockservice/AddPrintersActivity.java
rename to packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/AddPrintersActivity.java
index 8f1a9ed..2ea4e7d 100644
--- a/core/tests/coretests/src/android/print/mockservice/AddPrintersActivity.java
+++ b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/AddPrintersActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.print.mockservice;
+package com.android.printspooler.outofprocess.tests.mockservice;
import android.app.Activity;
import android.os.Bundle;
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/MockPrintService.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/MockPrintService.java
new file mode 100644
index 0000000..3a23113
--- /dev/null
+++ b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/MockPrintService.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 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.printspooler.outofprocess.tests.mockservice;
+
+public class MockPrintService extends StubbablePrintService {
+
+ private static final Object sLock = new Object();
+
+ private static PrintServiceCallbacks sCallbacks;
+
+ public static void setCallbacks(PrintServiceCallbacks callbacks) {
+ synchronized (sLock) {
+ sCallbacks = callbacks;
+ }
+ }
+
+ @Override
+ protected PrintServiceCallbacks getCallbacks() {
+ synchronized (sLock) {
+ if (sCallbacks != null) {
+ sCallbacks.setService(this);
+ }
+ return sCallbacks;
+ }
+ }
+}
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/PrintServiceCallbacks.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/PrintServiceCallbacks.java
new file mode 100644
index 0000000..07baa0f
--- /dev/null
+++ b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/PrintServiceCallbacks.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 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.printspooler.outofprocess.tests.mockservice;
+
+import android.printservice.PrintJob;
+import android.printservice.PrintService;
+
+public abstract class PrintServiceCallbacks {
+
+ private PrintService mService;
+
+ public PrintService getService() {
+ return mService;
+ }
+
+ public void setService(PrintService service) {
+ mService = service;
+ }
+
+ public abstract PrinterDiscoverySessionCallbacks onCreatePrinterDiscoverySessionCallbacks();
+
+ public abstract void onRequestCancelPrintJob(PrintJob printJob);
+
+ public abstract void onPrintJobQueued(PrintJob printJob);
+}
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/PrinterDiscoverySessionCallbacks.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/PrinterDiscoverySessionCallbacks.java
new file mode 100644
index 0000000..5c1260c
--- /dev/null
+++ b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/PrinterDiscoverySessionCallbacks.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 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.printspooler.outofprocess.tests.mockservice;
+
+import android.os.CancellationSignal;
+import android.print.PrinterId;
+import android.printservice.CustomPrinterIconCallback;
+
+import java.util.List;
+
+public abstract class PrinterDiscoverySessionCallbacks {
+
+ private StubbablePrinterDiscoverySession mSession;
+
+ public void setSession(StubbablePrinterDiscoverySession session) {
+ mSession = session;
+ }
+
+ public StubbablePrinterDiscoverySession getSession() {
+ return mSession;
+ }
+
+ public abstract void onStartPrinterDiscovery(List<PrinterId> priorityList);
+
+ public abstract void onStopPrinterDiscovery();
+
+ public abstract void onValidatePrinters(List<PrinterId> printerIds);
+
+ public abstract void onStartPrinterStateTracking(PrinterId printerId);
+
+ public abstract void onRequestCustomPrinterIcon(PrinterId printerId,
+ CancellationSignal cancellationSignal, CustomPrinterIconCallback callback);
+
+ public abstract void onStopPrinterStateTracking(PrinterId printerId);
+
+ public abstract void onDestroy();
+}
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/StubbablePrintService.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/StubbablePrintService.java
new file mode 100644
index 0000000..be9d19b
--- /dev/null
+++ b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/StubbablePrintService.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 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.printspooler.outofprocess.tests.mockservice;
+
+import android.printservice.PrintJob;
+import android.printservice.PrintService;
+import android.printservice.PrinterDiscoverySession;
+
+public abstract class StubbablePrintService extends PrintService {
+
+ @Override
+ public PrinterDiscoverySession onCreatePrinterDiscoverySession() {
+ PrintServiceCallbacks callbacks = getCallbacks();
+ if (callbacks != null) {
+ return new StubbablePrinterDiscoverySession(this,
+ getCallbacks().onCreatePrinterDiscoverySessionCallbacks());
+ }
+ return null;
+ }
+
+ @Override
+ public void onRequestCancelPrintJob(PrintJob printJob) {
+ PrintServiceCallbacks callbacks = getCallbacks();
+ if (callbacks != null) {
+ callbacks.onRequestCancelPrintJob(printJob);
+ }
+ }
+
+ @Override
+ public void onPrintJobQueued(PrintJob printJob) {
+ PrintServiceCallbacks callbacks = getCallbacks();
+ if (callbacks != null) {
+ callbacks.onPrintJobQueued(printJob);
+ }
+ }
+
+ protected abstract PrintServiceCallbacks getCallbacks();
+}
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/StubbablePrinterDiscoverySession.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/StubbablePrinterDiscoverySession.java
new file mode 100644
index 0000000..a828cd6
--- /dev/null
+++ b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/StubbablePrinterDiscoverySession.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 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.printspooler.outofprocess.tests.mockservice;
+
+import android.os.CancellationSignal;
+import android.print.PrinterId;
+import android.printservice.CustomPrinterIconCallback;
+import android.printservice.PrintService;
+import android.printservice.PrinterDiscoverySession;
+import android.support.annotation.NonNull;
+
+import java.util.List;
+
+public class StubbablePrinterDiscoverySession extends PrinterDiscoverySession {
+ private final PrintService mService;
+ private final PrinterDiscoverySessionCallbacks mCallbacks;
+
+ public StubbablePrinterDiscoverySession(PrintService service,
+ PrinterDiscoverySessionCallbacks callbacks) {
+ mService = service;
+ mCallbacks = callbacks;
+ if (mCallbacks != null) {
+ mCallbacks.setSession(this);
+ }
+ }
+
+ public PrintService getService() {
+ return mService;
+ }
+
+ @Override
+ public void onStartPrinterDiscovery(@NonNull List<PrinterId> priorityList) {
+ if (mCallbacks != null) {
+ mCallbacks.onStartPrinterDiscovery(priorityList);
+ }
+ }
+
+ @Override
+ public void onStopPrinterDiscovery() {
+ if (mCallbacks != null) {
+ mCallbacks.onStopPrinterDiscovery();
+ }
+ }
+
+ @Override
+ public void onValidatePrinters(@NonNull List<PrinterId> printerIds) {
+ if (mCallbacks != null) {
+ mCallbacks.onValidatePrinters(printerIds);
+ }
+ }
+
+ @Override
+ public void onStartPrinterStateTracking(@NonNull PrinterId printerId) {
+ if (mCallbacks != null) {
+ mCallbacks.onStartPrinterStateTracking(printerId);
+ }
+ }
+
+ @Override
+ public void onRequestCustomPrinterIcon(@NonNull PrinterId printerId,
+ @NonNull CancellationSignal cancellationSignal,
+ @NonNull CustomPrinterIconCallback callback) {
+ if (mCallbacks != null) {
+ mCallbacks.onRequestCustomPrinterIcon(printerId, cancellationSignal, callback);
+ }
+ }
+
+ @Override
+ public void onStopPrinterStateTracking(@NonNull PrinterId printerId) {
+ if (mCallbacks != null) {
+ mCallbacks.onStopPrinterStateTracking(printerId);
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ if (mCallbacks != null) {
+ mCallbacks.onDestroy();
+ }
+ }
+}
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 971f734..20e3f6f 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi-verbinding het misluk"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Stawingsprobleem"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Nie binne ontvangs nie"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Sal nie outomaties koppel nie"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Geen internettoegang nie"</string>
<string name="saved_network" msgid="4352716707126620811">"Gestoor deur <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Gekoppel via Wi-Fi-assistent"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Gekoppel via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 9216d85..e4455a4 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"የWiFi ግንኙነት መሰናከል"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"የማረጋገጫ ችግር"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"በክልል ውስጥ የለም"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"በራስ-ሰር አይገናኝም"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"ምንም የበይነመረብ መዳረሻ ያለም"</string>
<string name="saved_network" msgid="4352716707126620811">"የተቀመጠው በ<xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"በWi‑Fi ረዳት አማካኝነት ተገናኝቷል"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"በ%1$s በኩል መገናኘት"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 34fc456..24b51ec 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"أخفق اتصال WiFi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"حدثت مشكلة في المصادقة"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"ليست في النطاق"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"لن يتم الاتصال تلقائيًا"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"لا يتوفر اتصال بالإنترنت"</string>
<string name="saved_network" msgid="4352716707126620811">"تم الحفظ بواسطة <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"تم التوصيل عبر مساعد Wi-Fi"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"تم الاتصال عبر %1$s"</string>
diff --git a/packages/SettingsLib/res/values-az-rAZ/strings.xml b/packages/SettingsLib/res/values-az-rAZ/strings.xml
index 99875c3..5eb7a00 100644
--- a/packages/SettingsLib/res/values-az-rAZ/strings.xml
+++ b/packages/SettingsLib/res/values-az-rAZ/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi Bağlantı Uğursuzluğu"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Autentifikasiya problemi"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Diapazonda deyil"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Avtomatik qoşulmayacaq"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"İnternet girişi yoxdur"</string>
<string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> tərəfindən saxlandı"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi köməkçisi vasitəsilə qoşulub"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s vasitəsilə qoşuludur"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index e3cf2c9..1acc445 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi veza je otkazala"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problem sa potvrdom autentičnosti"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Nije u opsegu"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Automatsko povezivanje nije uspelo"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Nema pristupa internetu"</string>
<string name="saved_network" msgid="4352716707126620811">"Sačuvao/la je <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Povezano preko Wi‑Fi pomoćnika"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Veza je uspostavljena preko pristupne tačke %1$s"</string>
diff --git a/packages/SettingsLib/res/values-be-rBY/strings.xml b/packages/SettingsLib/res/values-be-rBY/strings.xml
index 5c2d860..0e3faca 100644
--- a/packages/SettingsLib/res/values-be-rBY/strings.xml
+++ b/packages/SettingsLib/res/values-be-rBY/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Збой падлучэння Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Праблема аўтэнтыфікацыі"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Не ў зоне дасягальнасці"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Не будзе аўтаматычна падключацца"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Няма доступу да інтэрнэту"</string>
<string name="saved_network" msgid="4352716707126620811">"Хто захаваў: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Падлучана праз памочніка Wi‑Fi"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Падлучана праз %1$s"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index e4e2eef..1d618d7 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Неуспешна връзка с Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Проблем при удостоверяването"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Извън обхват"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Няма да се свърже автоматично"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Няма достъп до интернет"</string>
<string name="saved_network" msgid="4352716707126620811">"Запазено от <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Установена е връзка чрез помощника за Wi-Fi"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Установена е връзка през „%1$s“"</string>
diff --git a/packages/SettingsLib/res/values-bn-rBD/strings.xml b/packages/SettingsLib/res/values-bn-rBD/strings.xml
index b5ba7d3..c094d27 100644
--- a/packages/SettingsLib/res/values-bn-rBD/strings.xml
+++ b/packages/SettingsLib/res/values-bn-rBD/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi সংযোগের ব্যর্থতা"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"প্রমাণীকরণ সমস্যা"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"পরিসরের মধ্যে নয়"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"স্বয়ংক্রিয়ভাবে সংযোগ করবে না"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"কোনো ইন্টারনেট অ্যাক্সেস নেই"</string>
<string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> দ্বারা সংরক্ষিত"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"ওয়াই-ফাই সহায়ক-এর মাধ্যমে সংযুক্ত হয়েছে"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s মাধ্যমে সংযুক্ত হয়েছে"</string>
diff --git a/packages/SettingsLib/res/values-bs-rBA/strings.xml b/packages/SettingsLib/res/values-bs-rBA/strings.xml
index 1180e0e..b7a7d29 100644
--- a/packages/SettingsLib/res/values-bs-rBA/strings.xml
+++ b/packages/SettingsLib/res/values-bs-rBA/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Greška pri povezivanju na Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problem pri provjeri vjerodostojnosti."</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Nije u dometu"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Neće se automatski povezati"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Nema pristupa internetu"</string>
<string name="saved_network" msgid="4352716707126620811">"Sačuvao <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Povezani preko Wi-Fi pomoćnika"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Povezani preko %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index ca3ffb4..0d696d5 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Error de connexió Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problema d\'autenticació"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Fora de l\'abast"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"No es connectarà automàticament"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"No hi ha accés a Internet"</string>
<string name="saved_network" msgid="4352716707126620811">"Desat per <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Connectat mitjançant l\'assistent de Wi‑Fi"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Connectada mitjançant %1$s"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 0170ca0..486819a 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Selhání připojení Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problém s ověřením"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Mimo dosah"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Připojení nebude automaticky navázáno"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Nebyl zjištěn žádný přístup k internetu"</string>
<string name="saved_network" msgid="4352716707126620811">"Uloženo uživatelem <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Připojeno pomocí asistenta připojení Wi-Fi"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Připojeno prostřednictvím %1$s"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 1a5fb0d..af4bf05 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi-forbindelsesfejl"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problem med godkendelse"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Ikke inden for rækkevidde"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Der oprettes ikke automatisk forbindelse"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Ingen internetadgang"</string>
<string name="saved_network" msgid="4352716707126620811">"Gemt af <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Forbindelse via Wi-Fi-assistent"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Tilsluttet via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 48a8171..e4da20d 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WLAN-Verbindungsfehler"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Authentifizierungsproblem"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Nicht in Reichweite"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Kein automatischer Verbindungsaufbau"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Kein Internetzugriff"</string>
<string name="saved_network" msgid="4352716707126620811">"Gespeichert von <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Über WLAN-Assistenten verbunden"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Über %1$s verbunden"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 28b22be..715b6c4 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Αποτυχία σύνδεσης Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Πρόβλημα ελέγχου ταυτότητας"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Εκτός εμβέλειας"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Δεν θα συνδεθεί αυτόματα"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Δεν υπάρχει πρόσβαση στο διαδίκτυο"</string>
<string name="saved_network" msgid="4352716707126620811">"Αποθηκεύτηκε από <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Σύνδεση μέσω βοηθού Wi‑Fi"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Συνδέθηκε μέσω %1$s"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index d3873cd..f3bc540 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi Connection Failure"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Authentication problem"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Not in range"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Won\'t automatically connect"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"No Internet access"</string>
<string name="saved_network" msgid="4352716707126620811">"Saved by <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Connected via Wi‑Fi assistant"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Connected via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index d3873cd..f3bc540 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi Connection Failure"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Authentication problem"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Not in range"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Won\'t automatically connect"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"No Internet access"</string>
<string name="saved_network" msgid="4352716707126620811">"Saved by <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Connected via Wi‑Fi assistant"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Connected via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index d3873cd..f3bc540 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi Connection Failure"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Authentication problem"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Not in range"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Won\'t automatically connect"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"No Internet access"</string>
<string name="saved_network" msgid="4352716707126620811">"Saved by <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Connected via Wi‑Fi assistant"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Connected via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index c9129aa..8f91cb7 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Error de conexión Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problema de autenticación"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Fuera de alcance"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"No se conectará automáticamente"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"No se detectó acceso a Internet"</string>
<string name="saved_network" msgid="4352716707126620811">"Guardadas por <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Conexión por asistente de Wi-Fi"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Conexión a través de %1$s"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index b7df701..fc8da79 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Error de conexión Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Error de autenticación"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Fuera de rango"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"No se establecerá conexión automáticamente"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"No se ha detectado acceso a Internet"</string>
<string name="saved_network" msgid="4352716707126620811">"Guardadas por <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Conectado a través de asistente Wi‑Fi"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Conectado a través de %1$s"</string>
diff --git a/packages/SettingsLib/res/values-et-rEE/strings.xml b/packages/SettingsLib/res/values-et-rEE/strings.xml
index 72c71d5..d28818b 100644
--- a/packages/SettingsLib/res/values-et-rEE/strings.xml
+++ b/packages/SettingsLib/res/values-et-rEE/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi-ühenduse viga"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Autentimise probleem"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Pole vahemikus"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Automaatselt ei ühendata"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Interneti-ühendus puudub"</string>
<string name="saved_network" msgid="4352716707126620811">"Salvestas: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Ühendatud WiFi-abi kaudu"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Ühendatud üksuse %1$s kaudu"</string>
diff --git a/packages/SettingsLib/res/values-eu-rES/strings.xml b/packages/SettingsLib/res/values-eu-rES/strings.xml
index f641a56..a975971 100644
--- a/packages/SettingsLib/res/values-eu-rES/strings.xml
+++ b/packages/SettingsLib/res/values-eu-rES/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Ezin izan da konektatu Wi-Fi sarera"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Autentifikazio-arazoa"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Urrunegi"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Ez da konektatuko automatikoki"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Ezin da konektatu Internetera"</string>
<string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> aplikazioak gorde du"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi laguntzailearen bidez konektatuta"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s bidez konektatuta"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index f0ddc9b..8f613aa 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"اتصال Wi-Fi برقرار نشد"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"مشکل احراز هویت"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"در محدوده نیست"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"اتصال بهصورت خودکار انجام نمیشود"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"دسترسی به اینترنت وجود ندارد"</string>
<string name="saved_network" msgid="4352716707126620811">"ذخیرهشده توسط <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"متصل شده از طریق دستیار Wi-Fi"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"متصل از طریق %1$s"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 7d0e324..ea2a17f 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi-yhteysvirhe"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Todennusvirhe"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Ei kantoalueella"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Yhteyttä ei muodosteta automaattisesti"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Ei internetyhteyttä"</string>
<string name="saved_network" msgid="4352716707126620811">"Tallentaja: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Yhteys muodostettu Wi‑Fi-apurin kautta"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Yhdistetty seuraavan kautta: %1$s"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index a121144..3001a69 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Échec de connexion Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problème d\'authentification"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Hors de portée"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Reconnexion automatique impossible"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Aucun accès à Internet"</string>
<string name="saved_network" msgid="4352716707126620811">"Enregistrés par <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Connecté à l\'aide de l\'assistant Wi-Fi"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Connecté par %1$s"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 7a6c837..c00a030 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Échec de la connexion Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problème d\'authentification."</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Hors de portée"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Reconnexion automatique impossible"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Aucun accès à Internet"</string>
<string name="saved_network" msgid="4352716707126620811">"Enregistré par <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Connecté via l\'assistant Wi‑Fi"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Connecté via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-gl-rES/strings.xml b/packages/SettingsLib/res/values-gl-rES/strings.xml
index f679485..03cf223 100644
--- a/packages/SettingsLib/res/values-gl-rES/strings.xml
+++ b/packages/SettingsLib/res/values-gl-rES/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Erro na conexión wifi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problema de autenticación"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Non está dentro da zona de cobertura"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Non se conectará automaticamente"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Non hai acceso a Internet"</string>
<string name="saved_network" msgid="4352716707126620811">"Redes gardadas por <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Conectado ao asistente de wifi"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Conectado a través de %1$s"</string>
diff --git a/packages/SettingsLib/res/values-gu-rIN/strings.xml b/packages/SettingsLib/res/values-gu-rIN/strings.xml
index 4e6890c..737af54 100644
--- a/packages/SettingsLib/res/values-gu-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-gu-rIN/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi કનેક્શન નિષ્ફળ"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"પ્રમાણીકરણ સમસ્યા"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"રેન્જમાં નથી"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"આપમેળે કનેક્ટ કરશે નહીં"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"કોઈ ઇન્ટરનેટ ઍક્સેસ નથી"</string>
<string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> દ્વારા સચવાયું"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Wi-Fi સહાયક દ્વારા કનેક્ટ થયું"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s દ્વારા કનેક્ટ થયેલ"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index e4cd51f..efefca3 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"वाईफ़ाई कनेक्शन विफलता"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"प्रमाणीकरण समस्या"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"रेंज में नहीं"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"अपने आप कनेक्ट नहीं होगा"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"कोई इंटरनेट एक्सेस नहीं"</string>
<string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> के द्वारा सहेजा गया"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"वाई-फ़ाई सहायक के द्वारा कनेक्ट है"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s के द्वारा उपलब्ध"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 063184a..8990536 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Povezivanje s Wi-Fi-jem nije uspjelo"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problem u autentifikaciji"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Nije u rasponu"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Neće se povezati automatski"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Nema pristupa internetu"</string>
<string name="saved_network" msgid="4352716707126620811">"Spremljeno: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Povezani putem pomoćnika za Wi-Fi"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Povezano putem %1$s"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 0349f3f..c557161 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi-kapcsolati hiba"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Azonosítási probléma"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Hatókörön kívül"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Nem csatlakozik automatikusan"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Nincs internet-hozzáférés"</string>
<string name="saved_network" msgid="4352716707126620811">"Mentette: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Csatlakozva Wi‑Fi-segéddel"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Csatlakozva a következőn keresztül: %1$s"</string>
diff --git a/packages/SettingsLib/res/values-hy-rAM/strings.xml b/packages/SettingsLib/res/values-hy-rAM/strings.xml
index c4421f9..41556dc 100644
--- a/packages/SettingsLib/res/values-hy-rAM/strings.xml
+++ b/packages/SettingsLib/res/values-hy-rAM/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi կապի ձախողում"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Նույնականացման խնդիր"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Ընդգրկույթից դուրս է"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Ավտոմատ միացում չի կատարվի"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Ինտերնետ կապ չկա"</string>
<string name="saved_network" msgid="4352716707126620811">"Պահել է հետևյալ օգտվողը՝ <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Կապակցված է Wi‑Fi Օգնականի միջոցով"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Կապակցված է %1$s-ի միջոցով"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 24d9d7a..039457d 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Kegagalan Sambungan Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Masalah autentikasi"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Tidak dalam jangkauan"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Tidak akan tersambung otomatis"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Tidak ada akses internet"</string>
<string name="saved_network" msgid="4352716707126620811">"Disimpan oleh <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Terhubung melalui Asisten Wi-Fi"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Terhubung melalui %1$s"</string>
diff --git a/packages/SettingsLib/res/values-is-rIS/strings.xml b/packages/SettingsLib/res/values-is-rIS/strings.xml
index 01b340c..a3126bf 100644
--- a/packages/SettingsLib/res/values-is-rIS/strings.xml
+++ b/packages/SettingsLib/res/values-is-rIS/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi-tengingarvilla"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Vandamál við auðkenningu"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Ekkert samband"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Mun ekki tengjast sjálfkrafa"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Enginn netaðgangur"</string>
<string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> vistaði"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Tengt í gegnum Wi-Fi aðstoð"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Tengt í gegnum %1$s"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 6a1bf32..bff3bb7 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Errore connessione Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problema di autenticazione"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Fuori portata"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Non verrà eseguita la connessione automatica"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Nessun accesso a Internet"</string>
<string name="saved_network" msgid="4352716707126620811">"Salvata da <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Connesso tramite assistente Wi‑Fi"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Collegato tramite %1$s"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index fa2a663..4d794fc 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"כשל בחיבור Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"בעיית אימות"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"מחוץ לטווח"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"לא יתבצע חיבור באופן אוטומטי"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"אין גישה לאינטרנט"</string>
<string name="saved_network" msgid="4352716707126620811">"נשמר על ידי <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"מחובר באמצעות אסיסטנט ה-Wi-Fi"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"מחובר דרך %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 4ee077f..4c4eed0 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi接続エラー"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"認証に問題"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"圏外"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"自動的に接続されません"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"インターネットに接続していません"</string>
<string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g>で保存"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fiアシスタント経由で接続"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s経由で接続"</string>
diff --git a/packages/SettingsLib/res/values-ka-rGE/strings.xml b/packages/SettingsLib/res/values-ka-rGE/strings.xml
index 9de55b0..2142fa3 100644
--- a/packages/SettingsLib/res/values-ka-rGE/strings.xml
+++ b/packages/SettingsLib/res/values-ka-rGE/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi კავშირის შეფერხება"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"ავთენტიკაციის პრობლემა"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"არ არის დიაპაზონში"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"ავტომატურად დაკავშირება ვერ მოხერხდება"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"ინტერნეტთან კავშირი არ არის"</string>
<string name="saved_network" msgid="4352716707126620811">"შენახული <xliff:g id="NAME">%1$s</xliff:g>-ის მიერ"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"დაკავშირებულია Wi-Fi თანაშემწით"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s-ით დაკავშირებული"</string>
diff --git a/packages/SettingsLib/res/values-kk-rKZ/strings.xml b/packages/SettingsLib/res/values-kk-rKZ/strings.xml
index cac3c23..e00e820 100644
--- a/packages/SettingsLib/res/values-kk-rKZ/strings.xml
+++ b/packages/SettingsLib/res/values-kk-rKZ/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi байланысының қатесі"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Растау мәселесі"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Аумақта жоқ"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Автоматты қосылмайды"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Интернетпен байланыс жоқ"</string>
<string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> сақтаған"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi көмекшісі арқылы қосылу орындалды"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s арқылы қосылған"</string>
diff --git a/packages/SettingsLib/res/values-km-rKH/strings.xml b/packages/SettingsLib/res/values-km-rKH/strings.xml
index 3593c4a..b1a27ce 100644
--- a/packages/SettingsLib/res/values-km-rKH/strings.xml
+++ b/packages/SettingsLib/res/values-km-rKH/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"ការភ្ជាប់ WiFi បរាជ័យ"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"បញ្ហាក្នុងការផ្ទៀងផ្ទាត់"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"នៅក្រៅតំបន់"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"នឹងមិនភ្ជាប់ដោយស្វ័យប្រវត្តិទេ"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"មិនមានអ៊ីនធឺណិតទេ"</string>
<string name="saved_network" msgid="4352716707126620811">"បានរក្សាទុកដោយ <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"បានភ្ជាប់តាមរយៈជំនួយការ Wi‑Fi"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"បានភ្ជាប់តាមរយៈ %1$s"</string>
diff --git a/packages/SettingsLib/res/values-kn-rIN/strings.xml b/packages/SettingsLib/res/values-kn-rIN/strings.xml
index 6cfb043..312beae 100644
--- a/packages/SettingsLib/res/values-kn-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-kn-rIN/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi ಸಂಪರ್ಕ ವಿಫಲತೆ"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"ಪ್ರಮಾಣೀಕರಣ ಸಮಸ್ಯೆ"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"ವ್ಯಾಪ್ತಿಯಲ್ಲಿಲ್ಲ"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶವಿಲ್ಲ"</string>
<string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> ರಿಂದ ಉಳಿಸಲಾಗಿದೆ"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi ಸಹಾಯಕದ ಮೂಲಕ ಸಂಪರ್ಕಿತಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s ಮೂಲಕ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 892c330..72b5fe3 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi 연결 실패"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"인증 문제"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"범위 내에 없음"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"자동으로 연결되지 않습니다."</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"인터넷에 연결되어 있지 않습니다."</string>
<string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g>(으)로 저장됨"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi 도우미를 통해 연결됨"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s을(를) 통해 연결됨"</string>
diff --git a/packages/SettingsLib/res/values-ky-rKG/strings.xml b/packages/SettingsLib/res/values-ky-rKG/strings.xml
index ea1c964..a5386c8 100644
--- a/packages/SettingsLib/res/values-ky-rKG/strings.xml
+++ b/packages/SettingsLib/res/values-ky-rKG/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi туташуусу бузулду"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Аутентификация маселеси бар"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Тейлөө аймагында эмес"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Автоматтык түрдө туташпайт"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Интернетке туташпай турат"</string>
<string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> тарабынан сакталды"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi жардамчысы аркылуу туташып турат"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s аркылуу жеткиликтүү"</string>
diff --git a/packages/SettingsLib/res/values-lo-rLA/strings.xml b/packages/SettingsLib/res/values-lo-rLA/strings.xml
index b37f0fd..2dc4fd7 100644
--- a/packages/SettingsLib/res/values-lo-rLA/strings.xml
+++ b/packages/SettingsLib/res/values-lo-rLA/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"ການເຊື່ອມຕໍ່ WiFi ລົ້ມເຫຼວ"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"ບັນຫາການພິສູດຢືນຢັນ"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"ບໍ່ຢູ່ໃນໄລຍະທີ່ເຊື່ອມຕໍ່ໄດ້"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"ຈະບໍ່ເຊື່ອມຕໍ່ອັດຕະໂນມັດ"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"ບໍ່ມີການເຊື່ອມຕໍ່ອິນເຕີເນັດ"</string>
<string name="saved_network" msgid="4352716707126620811">"ບັນທຶກໂດຍ <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"ເຊື່ອມຕໍ່ຜ່ານ Wi‑Fi ຕົວຊ່ວຍແລ້ວ"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"ເຊື່ອມຕໍ່ຜ່ານ %1$s ແລ້ວ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 187c886..51fb7fa 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"„Wi-Fi“ ryšio triktis"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Autentifikavimo problema"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Ne diapazone"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Nebus automatiškai prisijungiama"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Nėra interneto ryšio"</string>
<string name="saved_network" msgid="4352716707126620811">"Išsaugojo <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Prisijungta naudojant „Wi‑Fi“ pagelbiklį"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Prisijungta naudojant „%1$s“"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 5e6fe01..9d4d055 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi savienojuma kļūme"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Autentificēšanas problēma"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Nav diapazona ietvaros"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Savienojums netiks izveidots automātiski"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Nav piekļuves internetam"</string>
<string name="saved_network" msgid="4352716707126620811">"Saglabāja: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Izveidots savienojums ar Wi‑Fi palīgu"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Savienots, izmantojot %1$s"</string>
diff --git a/packages/SettingsLib/res/values-mk-rMK/strings.xml b/packages/SettingsLib/res/values-mk-rMK/strings.xml
index a5c9ff2..7521250 100644
--- a/packages/SettingsLib/res/values-mk-rMK/strings.xml
+++ b/packages/SettingsLib/res/values-mk-rMK/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Поврзувањето преку Wi-Fi не успеа"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Проблем со автентикација"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Надвор од опсег"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Не може да се поврзе автоматски"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Нема пристап до Интернет"</string>
<string name="saved_network" msgid="4352716707126620811">"Зачувано од <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Поврзано преку помошник за Wi-Fismile"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Поврзано преку %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ml-rIN/strings.xml b/packages/SettingsLib/res/values-ml-rIN/strings.xml
index fed05c9..5983992 100644
--- a/packages/SettingsLib/res/values-ml-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-ml-rIN/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi കണക്ഷൻ പരാജയം"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"ആധികാരികമാക്കുന്നതിലെ പ്രശ്നം"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"പരിധിയിലില്ല"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"സ്വയമേവ കണക്റ്റുചെയ്യില്ല"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"ഇന്റർനെറ്റ് ആക്സസ്സ് ഇല്ല"</string>
<string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> സംരക്ഷിച്ചത്"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"വൈഫൈ അസിസ്റ്റന്റ് മുഖേന കണക്റ്റുചെയ്തു"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s വഴി ബന്ധിപ്പിച്ചു"</string>
diff --git a/packages/SettingsLib/res/values-mn-rMN/strings.xml b/packages/SettingsLib/res/values-mn-rMN/strings.xml
index 9746ca2..3d32aea 100644
--- a/packages/SettingsLib/res/values-mn-rMN/strings.xml
+++ b/packages/SettingsLib/res/values-mn-rMN/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi холболт амжилтгүй"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Гэрчлэлийн асуудал"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Хүрээнд байхгүй"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Автоматаар холбогдохгүй"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Интернэт холболт алга"</string>
<string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> хадгалсан"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Wi-Fi туслагчаар дамжуулан холбогдлоо"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s-р холбогдсон"</string>
diff --git a/packages/SettingsLib/res/values-mr-rIN/strings.xml b/packages/SettingsLib/res/values-mr-rIN/strings.xml
index 0e45af9..e81b5ae 100644
--- a/packages/SettingsLib/res/values-mr-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-mr-rIN/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi कनेक्शन अयशस्वी"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"प्रमाणीकरण समस्या"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"परिक्षेत्रामध्ये नाही"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"स्वयंचलितपणे कनेक्ट करणार नाही"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"इंटरनेट प्रवेश नाही"</string>
<string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> द्वारे जतन केले"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi सहाय्यक द्वारे कनेक्ट केले"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s द्वारे कनेक्ट केले"</string>
diff --git a/packages/SettingsLib/res/values-ms-rMY/strings.xml b/packages/SettingsLib/res/values-ms-rMY/strings.xml
index 2368d09..ccceb12 100644
--- a/packages/SettingsLib/res/values-ms-rMY/strings.xml
+++ b/packages/SettingsLib/res/values-ms-rMY/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Kegagalan Sambungan WiFi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Masalah pengesahan"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Tidak dalam liputan"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Tidak akan menyambung secara automatik"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Tiada akses Internet"</string>
<string name="saved_network" msgid="4352716707126620811">"Diselamatkan oleh <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Disambungkan melalui Pembantu Wi-Fi"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Disambungkan melalui %1$s"</string>
diff --git a/packages/SettingsLib/res/values-my-rMM/strings.xml b/packages/SettingsLib/res/values-my-rMM/strings.xml
index 92ab9b8..95711a4 100644
--- a/packages/SettingsLib/res/values-my-rMM/strings.xml
+++ b/packages/SettingsLib/res/values-my-rMM/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi ချိတ်ဆက်မှု မအောင်မြင်ပါ"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"စစ်မှန်ကြောင်းအတည်ပြုရန်၌ ပြသနာရှိခြင်း"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"စက်ကွင်းထဲတွင် မဟုတ်ပါ"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"အလိုအလျောက်ချိတ်ဆက်မည်မဟုတ်ပါ"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"အင်တာနက် ချိတ်ဆက်မှု မရှိပါ"</string>
<string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> မှသိမ်းဆည်းခဲ့သည်"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"ကြိုးမဲ့ကူညီသူမှတဆင့် ချိတ်ဆက်၏"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s မှတစ်ဆင့် ချိတ်ဆက်ထားသည်"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 1a053e2..69fcafc 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi-tilkoblingsfeil"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Autentiseringsproblem"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Utenfor område"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Kobler ikke til automatisk"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Ingen Internett-tilgang"</string>
<string name="saved_network" msgid="4352716707126620811">"Lagret av <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Koblet til via en Wi-Fi-assistent"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Tilkoblet via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ne-rNP/strings.xml b/packages/SettingsLib/res/values-ne-rNP/strings.xml
index 34ede3c..0a7cdab 100644
--- a/packages/SettingsLib/res/values-ne-rNP/strings.xml
+++ b/packages/SettingsLib/res/values-ne-rNP/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"वाईफाई जडान असफल"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"प्रमाणीकरण समस्या"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"दायराभित्र छैन"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"स्वतः जडान हुने छैन"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"इन्टरनेट माथिको पहुँच छैन"</string>
<string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> द्वारा सुरक्षित गरियो"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Wi-Fi सहायक द्वारा जोडिएको"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s मार्फत जडित"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 0f19008..27478a4 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wifi-verbinding mislukt"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Authenticatieprobleem"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Niet binnen bereik"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Er wordt niet automatisch verbinding gemaakt"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Geen internettoegang"</string>
<string name="saved_network" msgid="4352716707126620811">"Opgeslagen door <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Verbonden via wifi-assistent"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Verbonden via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-pa-rIN/strings.xml b/packages/SettingsLib/res/values-pa-rIN/strings.xml
index ebaf067..d06a88f 100644
--- a/packages/SettingsLib/res/values-pa-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-pa-rIN/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi ਕਨੈਕਸ਼ਨ ਅਸਫਲਤਾ"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"ਪ੍ਰਮਾਣੀਕਰਨ ਸਮੱਸਿਆ"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"ਰੇਂਜ ਵਿੱਚ ਨਹੀਂ ਹੈ"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਜਾਵੇਗਾ"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"ਕੋਈ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ"</string>
<string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> ਵੱਲੋਂ ਸੁਰੱਖਿਅਤ ਕੀਤਾ"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi ਸਹਾਇਕ ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤਾ"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤਾ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 1dbc475..3c09335 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Błąd połączenia Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problem z uwierzytelnianiem"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Poza zasięgiem"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Nie można połączyć automatycznie"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Brak dostępu do internetu"</string>
<string name="saved_network" msgid="4352716707126620811">"Zapisane przez: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Połączono przez Asystenta Wi‑Fi"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Połączono przez %1$s"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 20115a0..1b46c84 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Falha de conexão Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problema de autenticação"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Fora do alcance"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Não se conectará automaticamente"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Sem acesso à Internet"</string>
<string name="saved_network" msgid="4352716707126620811">"Salvas por <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Conectado via assistente de Wi‑Fi"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Conectado via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 222c2c7..c8be0da 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Falha de ligação Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problema de autenticação"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Fora do alcance"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Não é efetuada uma ligação automaticamente"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Sem acesso à Internet"</string>
<string name="saved_network" msgid="4352716707126620811">"Guardada por <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Ligado através do Assistente de Wi‑Fi"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Ligado através de %1$s"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 20115a0..1b46c84 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Falha de conexão Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problema de autenticação"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Fora do alcance"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Não se conectará automaticamente"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Sem acesso à Internet"</string>
<string name="saved_network" msgid="4352716707126620811">"Salvas por <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Conectado via assistente de Wi‑Fi"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Conectado via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 08c9fb4..b022163 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Eroare de conexiune Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problemă la autentificare"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"În afara ariei de acoperire"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Nu se va conecta automat"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Nu există acces la internet"</string>
<string name="saved_network" msgid="4352716707126620811">"Salvată de <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Conexiune realizată printr-un asistent Wi-Fi"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Conectată prin %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index cd7de12..3a4e85b 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Ошибка подключения Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Ошибка аутентификации"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Недоступна"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Подключение не будет выполняться автоматически"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Отсутствует подключение к Интернету"</string>
<string name="saved_network" msgid="4352716707126620811">"Кто сохранил: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Установлено подключение через Ассистента Wi-Fi"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Подключено к %1$s"</string>
diff --git a/packages/SettingsLib/res/values-si-rLK/strings.xml b/packages/SettingsLib/res/values-si-rLK/strings.xml
index f59178f..24779b3 100644
--- a/packages/SettingsLib/res/values-si-rLK/strings.xml
+++ b/packages/SettingsLib/res/values-si-rLK/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi සම්බන්ධතාව අසාර්ථකයි"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"සත්යාපනයේ ගැටලුවකි"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"පරාසයේ නැත"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"ස්වයංක්රිය නැවත සම්බන්ධ නොවනු ඇත"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"අන්තර්ජාල ප්රවේශය නැත"</string>
<string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> විසින් සුරකින ලදී"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi සහායක හරහා සම්බන්ධ කරන ලදි"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s හරහා සම්බන්ධ විය"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 8b70d43..e8db85b 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Zlyhanie pripojenia Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problém s overením totožnosti"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Mimo dosah"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Nedôjde k automatickému pripojeniu"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Žiadny prístup k internetu"</string>
<string name="saved_network" msgid="4352716707126620811">"Uložil(a) <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Pripojené pomocou Asistenta Wi-Fi"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Pripojené prostredníctvom %1$s"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 77a580e..d908e6d 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Povezava prek Wi-Fi-ja ni uspela"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Težava s preverjanjem pristnosti"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Ni v obsegu"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Samodejna vnovična vzpostavitev povezave se ne bo izvedla"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Ni dostopa do interneta"</string>
<string name="saved_network" msgid="4352716707126620811">"Shranil(-a): <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Povezava vzpostavljena prek pomočnika za Wi-Fi"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Vzpostavljena povezava prek: %1$s"</string>
diff --git a/packages/SettingsLib/res/values-sq-rAL/strings.xml b/packages/SettingsLib/res/values-sq-rAL/strings.xml
index d158034..69318a1 100644
--- a/packages/SettingsLib/res/values-sq-rAL/strings.xml
+++ b/packages/SettingsLib/res/values-sq-rAL/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Dështim i lidhjes WiFi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problem me vërtetimin"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Nuk është brenda rrezes"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Nuk do të lidhet automatikisht"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Nuk ka qsaje në internet"</string>
<string name="saved_network" msgid="4352716707126620811">"E ruajtur nga <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"I lidhur nëpërmjet ndihmësit të Wi‑Fi"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"E lidhur përmes %1$s"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index ea7ef9dd..459d92e 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi веза је отказала"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Проблем са потврдом аутентичности"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Није у опсегу"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Аутоматско повезивање није успело"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Нема приступа интернету"</string>
<string name="saved_network" msgid="4352716707126620811">"Сачувао/ла је <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Повезано преко Wi‑Fi помоћника"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Веза је успостављена преко приступне тачке %1$s"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 9ed9459..d52882a 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi-anslutningsfel"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Autentiseringsproblem"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Utom räckhåll"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Det går inte att ansluta automatiskt"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Ingen internetåtkomst"</string>
<string name="saved_network" msgid="4352716707126620811">"Sparades av <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Ansluten via Wi-Fi-assistent"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Anslutet via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 124a101..6f2e0cb 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Haikuweza Kuunganisha kwenye WiFi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Tatizo la uthibitishaji"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Haiko karibu"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Haiwezi kuunganisha kiotomatiki"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Hakuna muunganisho wa Intaneti"</string>
<string name="saved_network" msgid="4352716707126620811">"Ilihifadhiwa na <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Imeunganishwa kupitia Kisaidizi cha Wi-Fi"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Imeunganishwa kupitia %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ta-rIN/strings.xml b/packages/SettingsLib/res/values-ta-rIN/strings.xml
index 96add0a..5f218e0 100644
--- a/packages/SettingsLib/res/values-ta-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-ta-rIN/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"வைஃபை இணைப்பில் தோல்வி"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"அங்கீகரிப்புச் சிக்கல்"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"தொடர்பு எல்லையில் இல்லை"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"தானாக இணைக்கப்படாது"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"இணைய அணுகல் இல்லை"</string>
<string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> சேமித்தது"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"வைஃபை அசிஸ்டண்ட் மூலம் இணைக்கப்பட்டது"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s வழியாக இணைக்கப்பட்டது"</string>
diff --git a/packages/SettingsLib/res/values-te-rIN/strings.xml b/packages/SettingsLib/res/values-te-rIN/strings.xml
index dfdaff1..0bd77f7 100644
--- a/packages/SettingsLib/res/values-te-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-te-rIN/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi కనెక్షన్ వైఫల్యం"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"ప్రామాణీకరణ సమస్య"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"పరిధిలో లేదు"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"స్వయంచాలకంగా కనెక్ట్ కాదు"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"ఇంటర్నెట్ ప్రాప్యత లేదు"</string>
<string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> ద్వారా సేవ్ చేయబడింది"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi సహాయకం ద్వారా కనెక్ట్ చేయబడింది"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s ద్వారా కనెక్ట్ చేయబడింది"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 5409801..d8ee2f3 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"การเชื่อมต่อ Wi-Fi ล้มเหลว"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"ปัญหาในการตรวจสอบสิทธิ์"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"ไม่อยู่ในพื้นที่ให้บริการ"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"จะไม่เชื่อมต่อโดยอัตโนมัติ"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"ไม่สามารถเข้าถึงอินเทอร์เน็ต"</string>
<string name="saved_network" msgid="4352716707126620811">"บันทึกโดย <xliff:g id="NAME">%1$s</xliff:g> แล้ว"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"เชื่อมต่อผ่านตัวช่วย Wi-Fi อยู่"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"เชื่อมต่อผ่าน %1$s แล้ว"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 482fa1a..9b9d26f 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Pagkabigo ng Koneksyon sa WiFi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problema sa pagpapatotoo"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Wala sa sakop"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Hindi awtomatikong kokonekta"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Walang access sa Internet"</string>
<string name="saved_network" msgid="4352716707126620811">"Na-save ni <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Nakakonekta sa pamamagitan ng Wi‑Fi assistant"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Nakakonekta sa pamamagitan ng %1$s"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 643b710..e3e50d5 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Kablosuz Bağlantı Hatası"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Kimlik doğrulama sorunu"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Kapsama alanı dışında"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Otomatik olarak bağlanma"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"İnternet erişimi yok"</string>
<string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> tarafından kaydedildi"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Kablosuz bağlantı yardımcısıyla bağlandı"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s üzerinden bağlı"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 74c3585..b1fbef5 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Помилка з’єднання Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Проблема з автентифікацією"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Не в діапазоні"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Не під’єднуватиметься автоматично"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Немає доступу до Інтернету"</string>
<string name="saved_network" msgid="4352716707126620811">"Збережено додатком <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Під’єднано через Диспетчер Wi-Fi-з’єднання"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Під’єднано через %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ur-rPK/strings.xml b/packages/SettingsLib/res/values-ur-rPK/strings.xml
index 4fc5212..322b468 100644
--- a/packages/SettingsLib/res/values-ur-rPK/strings.xml
+++ b/packages/SettingsLib/res/values-ur-rPK/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi کنکشن کی ناکامی"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"توثیق کا مسئلہ"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"رینج میں نہیں ہے"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"خودکار طور پر منسلک نہیں ہو گا"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"انٹرنیٹ تک کوئی رسائی نہیں"</string>
<string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> کی جانب سے محفوظ کردہ"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi اسسٹنٹ کے ذریعے منسلک ہے"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"منسلک بذریعہ %1$s"</string>
diff --git a/packages/SettingsLib/res/values-uz-rUZ/strings.xml b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
index c37738e..09ac4b1 100644
--- a/packages/SettingsLib/res/values-uz-rUZ/strings.xml
+++ b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi ulanishini o‘rnatib bo‘lmadi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Tasdiqdan o‘tishda muammo"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Aloqada emas"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Avtomatik ravishda ulanilmaydi"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Internet aloqasi yo‘q"</string>
<string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> tomonidan saqlangan"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi yordamchisi orqali ulangan"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s orqali ulangan"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 030cfdd..fa41b45 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Lỗi kết nối WiFi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Sự cố xác thực"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Ngoài vùng phủ sóng"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Sẽ không tự động kết nối"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Không có quyền truy cập Internet"</string>
<string name="saved_network" msgid="4352716707126620811">"Được lưu bởi <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Được kết nối qua trình hỗ trợ Wi‑Fi"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Được kết nối qua %1$s"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 2e515da..152e78e 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WLAN 连接失败"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"身份验证出现问题"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"不在范围内"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"无法自动连接"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"无法连接到互联网"</string>
<string name="saved_network" msgid="4352716707126620811">"已通过<xliff:g id="NAME">%1$s</xliff:g>保存"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"已连接(通过 WLAN 助手)"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"已通过%1$s连接"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 7d7e396..f6c2f93 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi 連線失敗"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"驗證問題"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"超出可用範圍"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"不會自動連線"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"無法偵測互聯網連線"</string>
<string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> 的儲存"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"已透過 Wi-Fi 小幫手連線"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"已透過 %1$s 連線"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 7ca5aaf..df9238a 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi 連線失敗"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"驗證問題"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"不在有效範圍內"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"無法自動連線"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"沒有可用的網際網路連線"</string>
<string name="saved_network" msgid="4352716707126620811">"由<xliff:g id="NAME">%1$s</xliff:g>儲存"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"已透過 Wi‑Fi 小幫手連線"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"已透過 %1$s 連線"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 67c0687..fc9e81f 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -28,10 +28,8 @@
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Ukwehlulekla koxhumo le-WiFi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Inkinga yokufakazela ubuqiniso"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Ayikho ebubanzini"</string>
- <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
- <skip />
- <!-- no translation found for wifi_no_internet (3880396223819116454) -->
- <skip />
+ <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Ngeke ize ixhumeke ngokuzenzakalela"</string>
+ <string name="wifi_no_internet" msgid="3880396223819116454">"Akukho ukufinyelela ku-inthanethi"</string>
<string name="saved_network" msgid="4352716707126620811">"Kulondolozwe ngu-<xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_wfa" msgid="3805736726317410714">"Ixhunywe ngomsizi we-Wi-FI"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Kuxhumeke nge-%1$s"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
index a8f286d..ce16336 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
@@ -5,7 +5,7 @@
* 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
+ * 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,
@@ -32,7 +32,6 @@
private static final String TAG = "CategoryManager";
private static CategoryManager sInstance;
-
private final InterestingConfigChanges mInterestingConfigChanges;
// Tile cache (key: <packageName, activityName>, value: tile)
@@ -43,20 +42,21 @@
private List<DashboardCategory> mCategories;
- public static CategoryManager get() {
+ public static CategoryManager get(Context context) {
if (sInstance == null) {
- sInstance = new CategoryManager();
+ sInstance = new CategoryManager(context);
}
return sInstance;
}
- CategoryManager() {
- mInterestingConfigChanges = new InterestingConfigChanges();
+ CategoryManager(Context context) {
mTileByComponentCache = new ArrayMap<>();
mCategoryByKeyMap = new ArrayMap<>();
+ mInterestingConfigChanges = new InterestingConfigChanges();
+ mInterestingConfigChanges.applyNewConfig(context.getResources());
}
- public DashboardCategory getTilesByCategory(Context context, String categoryKey) {
+ public synchronized DashboardCategory getTilesByCategory(Context context, String categoryKey) {
tryInitCategories(context);
final DashboardCategory category = mCategoryByKeyMap.get(categoryKey);
@@ -66,19 +66,19 @@
return category;
}
- public List<DashboardCategory> getCategories(Context context) {
+ public synchronized List<DashboardCategory> getCategories(Context context) {
tryInitCategories(context);
return mCategories;
}
- public void reloadAllCategoriesForConfigChange(Context context) {
- if (mInterestingConfigChanges.applyNewConfig(context.getResources())) {
- mCategories = null;
- tryInitCategories(context);
- }
+ public synchronized void reloadAllCategories(Context context) {
+ final boolean forceClearCache = mInterestingConfigChanges.applyNewConfig(
+ context.getResources());
+ mCategories = null;
+ tryInitCategories(context, forceClearCache);
}
- public void updateCategoryFromBlacklist(Set<ComponentName> tileBlacklist) {
+ public synchronized void updateCategoryFromBlacklist(Set<ComponentName> tileBlacklist) {
if (mCategories == null) {
Log.w(TAG, "Category is null, skipping blacklist update");
}
@@ -93,9 +93,17 @@
}
}
- private void tryInitCategories(Context context) {
+ private synchronized void tryInitCategories(Context context) {
+ // Keep cached tiles by default. The cache is only invalidated when InterestingConfigChange
+ // happens.
+ tryInitCategories(context, false /* forceClearCache */);
+ }
+
+ private synchronized void tryInitCategories(Context context, boolean forceClearCache) {
if (mCategories == null) {
- mTileByComponentCache.clear();
+ if (forceClearCache) {
+ mTileByComponentCache.clear();
+ }
mCategoryByKeyMap.clear();
mCategories = TileUtils.getCategories(context, mTileByComponentCache,
false /* categoryDefinedInManifest */);
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index 50867eb..bad7ba4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -185,7 +185,7 @@
return false;
}
if (isDashboardFeatureEnabled()) {
- final DashboardCategory homepageCategories = CategoryManager.get()
+ final DashboardCategory homepageCategories = CategoryManager.get(this)
.getTilesByCategory(this, CategoryKey.CATEGORY_HOMEPAGE);
return homepageCategories.containsComponent(componentName);
} else {
@@ -429,12 +429,12 @@
private final CategoryManager mCategoryManager;
public CategoriesUpdateTask() {
- mCategoryManager = CategoryManager.get();
+ mCategoryManager = CategoryManager.get(SettingsDrawerActivity.this);
}
@Override
protected Void doInBackground(Void... params) {
- mCategoryManager.reloadAllCategoriesForConfigChange(SettingsDrawerActivity.this);
+ mCategoryManager.reloadAllCategories(SettingsDrawerActivity.this);
return null;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java
index e1216a1..602d135 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java
@@ -69,8 +69,8 @@
}
public void updateHomepageCategories() {
- DashboardCategory category =
- CategoryManager.get().getTilesByCategory(mActivity, CategoryKey.CATEGORY_HOMEPAGE);
+ final DashboardCategory category = CategoryManager.get(mActivity)
+ .getTilesByCategory(mActivity, CategoryKey.CATEGORY_HOMEPAGE);
mItems.clear();
// Spacer.
mItems.add(null);
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java
index 495771a..f98667d 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java
@@ -14,12 +14,10 @@
package com.android.systemui.plugins;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -33,12 +31,10 @@
import com.android.internal.annotations.VisibleForTesting;
-import dalvik.system.PathClassLoader;
-
import java.util.ArrayList;
import java.util.List;
-public class PluginInstanceManager<T extends Plugin> extends BroadcastReceiver {
+public class PluginInstanceManager<T extends Plugin> {
private static final boolean DEBUG = false;
@@ -57,20 +53,21 @@
final PluginHandler mPluginHandler;
private final boolean isDebuggable;
private final PackageManager mPm;
- private final ClassLoaderFactory mClassLoaderFactory;
+ private final PluginManager mManager;
PluginInstanceManager(Context context, String action, PluginListener<T> listener,
- boolean allowMultiple, Looper looper, int version) {
+ boolean allowMultiple, Looper looper, int version, PluginManager manager) {
this(context, context.getPackageManager(), action, listener, allowMultiple, looper, version,
- Build.IS_DEBUGGABLE, new ClassLoaderFactory());
+ manager, Build.IS_DEBUGGABLE);
}
@VisibleForTesting
PluginInstanceManager(Context context, PackageManager pm, String action,
PluginListener<T> listener, boolean allowMultiple, Looper looper, int version,
- boolean debuggable, ClassLoaderFactory classLoaderFactory) {
+ PluginManager manager, boolean debuggable) {
mMainHandler = new MainHandler(Looper.getMainLooper());
mPluginHandler = new PluginHandler(looper);
+ mManager = manager;
mContext = context;
mPm = pm;
mAction = action;
@@ -78,44 +75,29 @@
mAllowMultiple = allowMultiple;
mVersion = version;
isDebuggable = debuggable;
- mClassLoaderFactory = classLoaderFactory;
}
- public void startListening() {
+ public void loadAll() {
if (DEBUG) Log.d(TAG, "startListening");
mPluginHandler.sendEmptyMessage(PluginHandler.QUERY_ALL);
- IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
- filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- filter.addDataScheme("package");
- mContext.registerReceiver(this, filter);
- filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
- mContext.registerReceiver(this, filter);
}
- public void stopListening() {
+ public void destroy() {
if (DEBUG) Log.d(TAG, "stopListening");
ArrayList<PluginInfo> plugins = new ArrayList<>(mPluginHandler.mPlugins);
for (PluginInfo plugin : plugins) {
mMainHandler.obtainMessage(MainHandler.PLUGIN_DISCONNECTED,
plugin.mPlugin).sendToTarget();
}
- mContext.unregisterReceiver(this);
}
- @Override
- public void onReceive(Context context, Intent intent) {
- if (DEBUG) Log.d(TAG, "onReceive " + intent);
- if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
- mPluginHandler.sendEmptyMessage(PluginHandler.QUERY_ALL);
- } else {
- Uri data = intent.getData();
- String pkgName = data.getEncodedSchemeSpecificPart();
- mPluginHandler.obtainMessage(PluginHandler.REMOVE_PKG, pkgName).sendToTarget();
- if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
- mPluginHandler.obtainMessage(PluginHandler.QUERY_PKG, pkgName).sendToTarget();
- }
- }
+ public void onPackageRemoved(String pkg) {
+ mPluginHandler.obtainMessage(PluginHandler.REMOVE_PKG, pkg).sendToTarget();
+ }
+
+ public void onPackageChange(String pkg) {
+ mPluginHandler.obtainMessage(PluginHandler.REMOVE_PKG, pkg).sendToTarget();
+ mPluginHandler.obtainMessage(PluginHandler.QUERY_PKG, pkg).sendToTarget();
}
public boolean checkAndDisable(String className) {
@@ -179,12 +161,6 @@
}
}
- static class ClassLoaderFactory {
- public ClassLoader createClassLoader(String path, ClassLoader base) {
- return new PathClassLoader(path, base);
- }
- }
-
private class PluginHandler extends Handler {
private static final int QUERY_ALL = 1;
private static final int QUERY_PKG = 2;
@@ -279,8 +255,7 @@
return null;
}
// Create our own ClassLoader so we can use our own code as the parent.
- ClassLoader classLoader = mClassLoaderFactory.createClassLoader(info.sourceDir,
- getClass().getClassLoader());
+ ClassLoader classLoader = mManager.getClassLoader(info.sourceDir, info.packageName);
Context pluginContext = new PluginContextWrapper(
mContext.createApplicationContext(info, 0), classLoader);
Class<?> pluginClass = Class.forName(cls, true, classLoader);
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginManager.java
index 4bf6494..686b4d4 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginManager.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginManager.java
@@ -14,7 +14,11 @@
package com.android.systemui.plugins;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
import android.os.Build;
import android.os.HandlerThread;
import android.os.Looper;
@@ -22,26 +26,31 @@
import com.android.internal.annotations.VisibleForTesting;
+import dalvik.system.PathClassLoader;
+
import java.lang.Thread.UncaughtExceptionHandler;
+import java.util.Map;
/**
* @see Plugin
*/
-public class PluginManager {
+public class PluginManager extends BroadcastReceiver {
private static PluginManager sInstance;
private final HandlerThread mBackgroundThread;
private final ArrayMap<PluginListener<?>, PluginInstanceManager> mPluginMap
= new ArrayMap<>();
+ private final Map<String, ClassLoader> mClassLoaders = new ArrayMap<>();
private final Context mContext;
private final PluginInstanceManagerFactory mFactory;
private final boolean isDebuggable;
private final PluginPrefs mPluginPrefs;
+ private ClassLoaderFilter mParentClassLoader;
private PluginManager(Context context) {
- this(context, new PluginInstanceManagerFactory(), Build.IS_DEBUGGABLE,
- Thread.getDefaultUncaughtExceptionHandler());
+ this(context, new PluginInstanceManagerFactory(),
+ Build.IS_DEBUGGABLE, Thread.getDefaultUncaughtExceptionHandler());
}
@VisibleForTesting
@@ -72,9 +81,12 @@
}
mPluginPrefs.addAction(action);
PluginInstanceManager p = mFactory.createPluginInstanceManager(mContext, action, listener,
- allowMultiple, mBackgroundThread.getLooper(), version);
- p.startListening();
+ allowMultiple, mBackgroundThread.getLooper(), version, this);
+ p.loadAll();
mPluginMap.put(listener, p);
+ if (mPluginMap.size() == 1) {
+ startListening();
+ }
}
public void removePluginListener(PluginListener<?> listener) {
@@ -83,7 +95,68 @@
return;
}
if (!mPluginMap.containsKey(listener)) return;
- mPluginMap.remove(listener).stopListening();
+ mPluginMap.remove(listener).destroy();
+ if (mPluginMap.size() == 0) {
+ stopListening();
+ }
+ }
+
+ private void startListening() {
+ IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addDataScheme("package");
+ mContext.registerReceiver(this, filter);
+ filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
+ mContext.registerReceiver(this, filter);
+ }
+
+ private void stopListening() {
+ mContext.unregisterReceiver(this);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
+ for (PluginInstanceManager manager : mPluginMap.values()) {
+ manager.loadAll();
+ }
+ } else {
+ Uri data = intent.getData();
+ String pkg = data.getEncodedSchemeSpecificPart();
+ clearClassLoader(pkg);
+ if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
+ for (PluginInstanceManager manager : mPluginMap.values()) {
+ manager.onPackageChange(pkg);
+ }
+ } else {
+ for (PluginInstanceManager manager : mPluginMap.values()) {
+ manager.onPackageRemoved(pkg);
+ }
+ }
+ }
+ }
+
+ public ClassLoader getClassLoader(String sourceDir, String pkg) {
+ if (mClassLoaders.containsKey(pkg)) {
+ return mClassLoaders.get(pkg);
+ }
+ ClassLoader classLoader = new PathClassLoader(sourceDir, getParentClassLoader());
+ mClassLoaders.put(pkg, classLoader);
+ return classLoader;
+ }
+
+ private void clearClassLoader(String pkg) {
+ mClassLoaders.remove(pkg);
+ }
+
+ ClassLoader getParentClassLoader() {
+ if (mParentClassLoader == null) {
+ // Lazily load this so it doesn't have any effect on devices without plugins.
+ mParentClassLoader = new ClassLoaderFilter(getClass().getClassLoader(),
+ "com.android.systemui.plugin");
+ }
+ return mParentClassLoader;
}
public static PluginManager getInstance(Context context) {
@@ -97,9 +170,29 @@
public static class PluginInstanceManagerFactory {
public <T extends Plugin> PluginInstanceManager createPluginInstanceManager(Context context,
String action, PluginListener<T> listener, boolean allowMultiple, Looper looper,
- int version) {
+ int version, PluginManager manager) {
return new PluginInstanceManager(context, action, listener, allowMultiple, looper,
- version);
+ version, manager);
+ }
+ }
+
+
+ // This allows plugins to include any libraries or copied code they want by only including
+ // classes from the plugin library.
+ private static class ClassLoaderFilter extends ClassLoader {
+ private final String mPackage;
+ private final ClassLoader mBase;
+
+ public ClassLoaderFilter(ClassLoader base, String pkg) {
+ super(ClassLoader.getSystemClassLoader());
+ mBase = base;
+ mPackage = pkg;
+ }
+
+ @Override
+ protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ if (!name.startsWith(mPackage)) super.loadClass(name, resolve);
+ return mBase.loadClass(name);
}
}
diff --git a/packages/SystemUI/res/values-bs-rBA/strings.xml b/packages/SystemUI/res/values-bs-rBA/strings.xml
index ca91502..6ce6dbe 100644
--- a/packages/SystemUI/res/values-bs-rBA/strings.xml
+++ b/packages/SystemUI/res/values-bs-rBA/strings.xml
@@ -238,7 +238,7 @@
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G prijenos podataka je pauzirano"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Mobilni podaci su pauzirani"</string>
<string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Prijenos podataka je pauziran"</string>
- <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Dostigli ste ograničenje za podatke koje ste postavili. Više ne koristite mobilne podatke.\n\nUkoliko nastavite koristiti mobilne podatke, mogući su troškovi za korištenje podataka."</string>
+ <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Dostigli ste ograničenje za prijenos podataka koje ste postavili. Više ne koristite mobilne podatke.\n\nUkoliko nastavite koristiti mobilne podatke, mogući su troškovi za prijenos podataka."</string>
<string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Nastavi"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Nema internet veze"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi veza aktivna"</string>
@@ -313,7 +313,7 @@
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Obavještenja"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Svjetiljka"</string>
<string name="quick_settings_cellular_detail_title" msgid="8575062783675171695">"Mobilni podaci"</string>
- <string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Korištenje podataka"</string>
+ <string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Prijenos podataka"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Preostala količina podataka"</string>
<string name="quick_settings_cellular_detail_over_limit" msgid="967669665390990427">"Prekoračeno"</string>
<string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"Iskorišteno <xliff:g id="DATA_USED">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 4a515a6..01f4d9e 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -241,7 +241,7 @@
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Data 4G jsou pozastavena"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Mobilní data jsou pozastavena"</string>
<string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Data jsou pozastavena"</string>
- <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Bylo dosaženo limitu dat. Používání mobilních dat bylo vypnuto.\n\nPokud jej obnovíte, mohou vám být účtovány poplatky za využití dat."</string>
+ <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Byl dosažen limit dat. Používání mobilních dat bylo vypnuto.\n\nPokud jej obnovíte, mohou vám být účtovány poplatky za využití dat."</string>
<string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Pokračovat"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Žádné přip. k internetu"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi: připojeno"</string>
diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index ee63681..23db2d0 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -237,7 +237,7 @@
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"ទិន្នន័យ 4G ត្រូវបានផ្អាក"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"ទិន្នន័យចល័តត្រូវបានផ្អាក"</string>
<string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"ទិន្នន័យត្រូវបានផ្អាក"</string>
- <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"បានឈានដល់កម្រិតទិន្នន័យដែលអ្នកបានកំណត់ហើយ។ អ្នកមិនអាចប្រើទិន្នន័យចល័តបានទៀតទេ។\n\nអាចនឹងគិតថ្លៃលើការប្រើទិន្នន័យ ប្រសិនបើអ្នកបន្តប្រើ។"</string>
+ <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"បានឈានដល់កម្រិតទិន្នន័យដែលអ្នកបានកំណត់ហើយ។ ឥឡូវអ្នកមិនប្រើទិន្នន័យចល័តទៀតទេ។\n\nអាចនឹងគិតថ្លៃលើការប្រើទិន្នន័យ ប្រសិនបើអ្នកបន្តប្រើឡើងវិញ។"</string>
<string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"បន្ត"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"គ្មានការតភ្ជាប់អ៊ីនធឺណិត"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"បានភ្ជាប់វ៉ាយហ្វាយ"</string>
diff --git a/packages/SystemUI/res/values-ky-rKG/strings.xml b/packages/SystemUI/res/values-ky-rKG/strings.xml
index 62775d1..91ebf7c 100644
--- a/packages/SystemUI/res/values-ky-rKG/strings.xml
+++ b/packages/SystemUI/res/values-ky-rKG/strings.xml
@@ -237,7 +237,7 @@
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G дайындары тындырылды"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Уюлдук дайындар тындырылды"</string>
<string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Дайындар тындырылды"</string>
- <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Сиз койгон дайындардын чегине жетти. Сиз мобилдик дайындарды колдонгон жоксуз.\n\nЭгер улантсаңыз, мобилдик дайындарды колдонгонуңуз үчүн акы алынышы мүмкүн."</string>
+ <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Трафик сиз койгон чекке жетти. Эми мобилдик дайындарды колдоно албайсыз.\n\nЭгер улантсаңыз, мобилдик дайындарды колдонгонуңуз үчүн акы алынышы мүмкүн."</string>
<string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Улантуу"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Интернет байланыш жок"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi байланышта"</string>
diff --git a/packages/SystemUI/res/values-my-rMM/strings.xml b/packages/SystemUI/res/values-my-rMM/strings.xml
index 4485883..905c865 100644
--- a/packages/SystemUI/res/values-my-rMM/strings.xml
+++ b/packages/SystemUI/res/values-my-rMM/strings.xml
@@ -237,7 +237,7 @@
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G data ခေတ္တရပ်တန့်သည်"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"cellular data ခေတ္တရပ်တန့်သည်"</string>
<string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"ဒေတာ ခေတ္တရပ်တန့်သည်"</string>
- <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"သင်သတ်မှတ်ထားသော ဒေတာကန့်သတ်ချက်သို့ ရောက်နေပါပြီ။ သင်သည် ဆယ်လူလာဒေတာကို သုံးနေတော့မည်မဟုတ်ပါ။\n\nသင်ဆက်လုပ်မည်ဆိုလျှင် ဒေတာသုံးစွဲမှုအတွက် အခငွေ ကျသင့်မှုရှိနိုင်ပါသည်။"</string>
+ <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"သင်သတ်မှတ်ထားသော ဒေတာကန့်သတ်ချက်သို့ ရောက်နေပါပြီ။ သင်သည် ဆယ်လူလာဒေတာကို အသုံးမပြုတော့ပါ။\n\nသင်ဆက်လုပ်မည်ဆိုလျှင် ဒေတာသုံးစွဲမှုအတွက် အခငွေ ကျသင့်မှုရှိနိုင်ပါသည်။"</string>
<string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"ပြန်ဆက်လုပ်ရန်"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"အင်တာနက်မရှိ"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"ကြိုးမဲ့ဆက်သွယ်မှု"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index f6e43dd..cc61b6e 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -570,7 +570,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Prehliadač"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontakty"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-mail"</string>
- <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Okamžité správy"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Čet"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Hudba"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalendár"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 1fbd6f4..4f8341f 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -237,7 +237,7 @@
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"หยุดการใช้ข้อมูล 4G ชั่วคราวแล้ว"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"หยุดการใช้ข้อมูลมือถือชั่วคราวแล้ว"</string>
<string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"หยุดการใช้ข้อมูลชั่วคราวแล้ว"</string>
- <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"คุณใช้อินเทอร์เน็ตเกินปริมาณที่กำหนดไว้แล้ว คุณจะไม่สามารถใช้ข้อมูลเครือข่ายมือถืออีก\n\nหากใช้ต่อ อาจมีค่าบริการสำหรับปริมาณการใช้อินเทอร์เน็ต"</string>
+ <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"คุณใช้อินเทอร์เน็ตเกินปริมาณที่กำหนดไว้ ระบบจะไม่ใช้เครือข่ายมือถือต่อไป\n\nหากใช้ต่อ อาจมีค่าบริการตามปริมาณการใช้อินเทอร์เน็ต"</string>
<string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"ทำต่อ"</string>
<string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"ไม่มีอินเทอร์เน็ต"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"เชื่อมต่อ WiFi แล้ว"</string>
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
index 076b5bc..e1d6a94 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
@@ -31,10 +31,9 @@
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.net.Uri;
-import android.os.Bundle;
import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings;
-
import com.android.systemui.statusbar.policy.BatteryController;
public class BatteryMeterDrawable extends Drawable implements
@@ -92,18 +91,17 @@
private int mLightModeBackgroundColor;
private int mLightModeFillColor;
- private final SettingObserver mSettingObserver = new SettingObserver();
+ private final SettingObserver mSettingObserver;
private final Context mContext;
- private final Handler mHandler;
private int mLevel = -1;
private boolean mPluggedIn;
private boolean mListening;
- public BatteryMeterDrawable(Context context, Handler handler, int frameColor) {
+ public BatteryMeterDrawable(Context context, int frameColor) {
mContext = context;
- mHandler = handler;
+ mSettingObserver = new SettingObserver(new Handler(mContext.getMainLooper()));
final Resources res = context.getResources();
TypedArray levels = res.obtainTypedArray(R.array.batterymeter_color_levels);
TypedArray colors = res.obtainTypedArray(R.array.batterymeter_color_values);
@@ -199,12 +197,7 @@
}
private void postInvalidate() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- invalidateSelf();
- }
- });
+ scheduleSelf(this::invalidateSelf, 0);
}
public void setBatteryController(BatteryController batteryController) {
@@ -506,8 +499,8 @@
}
private final class SettingObserver extends ContentObserver {
- public SettingObserver() {
- super(new Handler());
+ public SettingObserver(Handler handler) {
+ super(handler);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index d8b95cc..4f3ffde 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -49,7 +49,7 @@
defStyle, 0);
final int frameColor = atts.getColor(R.styleable.BatteryMeterView_frameColor,
context.getColor(R.color.batterymeter_frame_color));
- mDrawable = new BatteryMeterDrawable(context, new Handler(), frameColor);
+ mDrawable = new BatteryMeterDrawable(context, frameColor);
atts.recycle();
mSlotBattery = context.getString(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
index b61a81c..d89fbfd3c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
@@ -118,7 +118,7 @@
@Override
public Drawable getDrawable(Context context) {
BatteryMeterDrawable drawable =
- new BatteryMeterDrawable(context, new Handler(Looper.getMainLooper()),
+ new BatteryMeterDrawable(context,
context.getColor(R.color.batterymeter_frame_color));
drawable.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
drawable.onPowerSaveChanged(mPowerSave);
@@ -165,7 +165,7 @@
private final class BatteryDetail implements DetailAdapter, OnClickListener,
OnAttachStateChangeListener {
private final BatteryMeterDrawable mDrawable = new BatteryMeterDrawable(mHost.getContext(),
- new Handler(), mHost.getContext().getColor(R.color.batterymeter_frame_color));
+ mHost.getContext().getColor(R.color.batterymeter_frame_color));
private View mCurrentView;
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index a7132e5..7a1ea47 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -161,7 +161,7 @@
"com.android.systemui.statusbar.banner_action_cancel";
private static final String BANNER_ACTION_SETUP =
"com.android.systemui.statusbar.banner_action_setup";
- private static final String WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION
+ private static final String NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION
= "com.android.systemui.statusbar.work_challenge_unlocked_notification_action";
protected CommandQueue mCommandQueue;
@@ -217,14 +217,14 @@
protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
// public mode, private notifications, etc
- private boolean mLockscreenPublicMode = false;
+ private final SparseBooleanArray mLockscreenPublicMode = new SparseBooleanArray();
private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
private final SparseBooleanArray mUsersAllowingNotifications = new SparseBooleanArray();
private UserManager mUserManager;
private int mDensity;
- private KeyguardManager mKeyguardManager;
+ protected KeyguardManager mKeyguardManager;
private LockPatternUtils mLockPatternUtils;
// UI-specific methods
@@ -465,11 +465,11 @@
row.setUserExpanded(true);
if (!mAllowLockscreenRemoteInput) {
- if (isLockscreenPublicMode()) {
+ final int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
+ if (isLockscreenPublicMode(userId)) {
onLockedRemoteInput(row, view);
return true;
}
- final int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
if (mUserManager.getUserInfo(userId).isManagedProfile()
&& mKeyguardManager.isDeviceLocked(userId)) {
onLockedWorkRemoteInput(userId, row, view);
@@ -560,7 +560,7 @@
);
}
- } else if (WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION.equals(action)) {
+ } else if (NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION.equals(action)) {
final IntentSender intentSender = intent.getParcelableExtra(Intent.EXTRA_INTENT);
final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX);
if (intentSender != null) {
@@ -577,7 +577,6 @@
/* ignore */
}
}
- onWorkChallengeUnlocked();
}
}
};
@@ -585,12 +584,18 @@
private final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
+ final String action = intent.getAction();
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+
if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action) &&
isCurrentProfile(getSendingUserId())) {
mUsersAllowingPrivateNotifications.clear();
updateLockscreenNotificationSetting();
updateNotifications();
+ } else if (Intent.ACTION_DEVICE_LOCKED_CHANGED.equals(action)) {
+ if (userId != mCurrentUserId && isCurrentProfile(userId)) {
+ onWorkChallengeChanged();
+ }
}
}
};
@@ -815,7 +820,7 @@
mContext.registerReceiver(mBroadcastReceiver, filter);
IntentFilter internalFilter = new IntentFilter();
- internalFilter.addAction(WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION);
+ internalFilter.addAction(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);
internalFilter.addAction(BANNER_ACTION_CANCEL);
internalFilter.addAction(BANNER_ACTION_SETUP);
mContext.registerReceiver(mBroadcastReceiver, internalFilter, PERMISSION_SELF, null);
@@ -823,6 +828,7 @@
IntentFilter allUsersFilter = new IntentFilter();
allUsersFilter.addAction(
DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
+ allUsersFilter.addAction(Intent.ACTION_DEVICE_LOCKED_CHANGED);
mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL, allUsersFilter,
null, null);
updateCurrentProfilesCache();
@@ -1126,9 +1132,10 @@
@Override
public void onClick(View v) {
// If the user has security enabled, show challenge if the setting is changed.
- if (guts.hasImportanceChanged() && isLockscreenPublicMode() &&
- (mState == StatusBarState.KEYGUARD
- || mState == StatusBarState.SHADE_LOCKED)) {
+ if (guts.hasImportanceChanged()
+ && isLockscreenPublicMode(sbn.getUser().getIdentifier())
+ && (mState == StatusBarState.KEYGUARD
+ || mState == StatusBarState.SHADE_LOCKED)) {
OnDismissAction dismissAction = new OnDismissAction() {
@Override
public boolean onDismiss() {
@@ -1430,15 +1437,15 @@
/**
* Save the current "public" (locked and secure) state of the lockscreen.
*/
- public void setLockscreenPublicMode(boolean publicMode) {
- mLockscreenPublicMode = publicMode;
+ public void setLockscreenPublicMode(boolean publicMode, int userId) {
+ mLockscreenPublicMode.put(userId, publicMode);
}
- public boolean isLockscreenPublicMode() {
- return mLockscreenPublicMode;
+ public boolean isLockscreenPublicMode(int userId) {
+ return mLockscreenPublicMode.get(userId, false);
}
- protected void onWorkChallengeUnlocked() {}
+ protected void onWorkChallengeChanged() {}
/**
* Has the given user chosen to allow notifications to be shown even when the lockscreen is in
@@ -1496,8 +1503,9 @@
* If so, notifications should be hidden.
*/
@Override // NotificationData.Environment
- public boolean shouldHideNotifications(int userid) {
- return isLockscreenPublicMode() && !userAllowsNotificationsInPublic(userid);
+ public boolean shouldHideNotifications(int userId) {
+ return isLockscreenPublicMode(mCurrentUserId) && !userAllowsNotificationsInPublic(userId)
+ || (userId != mCurrentUserId && shouldHideNotifications(mCurrentUserId));
}
/**
@@ -1506,7 +1514,7 @@
*/
@Override // NotificationDate.Environment
public boolean shouldHideNotifications(String key) {
- return isLockscreenPublicMode()
+ return isLockscreenPublicMode(mCurrentUserId)
&& mNotificationData.getVisibilityOverride(key) == Notification.VISIBILITY_SECRET;
}
@@ -1514,8 +1522,8 @@
* Returns true if we're on a secure lockscreen.
*/
@Override // NotificationData.Environment
- public boolean onSecureLockScreen() {
- return isLockscreenPublicMode();
+ public boolean isSecurelyLocked(int userId) {
+ return isLockscreenPublicMode(userId);
}
public void onNotificationClear(StatusBarNotification notification) {
@@ -2076,8 +2084,7 @@
if (newIntent == null) {
return false;
}
- final Intent callBackIntent = new Intent(
- WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION);
+ final Intent callBackIntent = new Intent(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);
callBackIntent.putExtra(Intent.EXTRA_INTENT, intendSender);
callBackIntent.putExtra(Intent.EXTRA_INDEX, notificationKey);
callBackIntent.setPackage(mContext.getPackageName());
@@ -2276,14 +2283,16 @@
entry.row.setOnKeyguard(false);
entry.row.setSystemExpanded(visibleNotifications == 0 && !childNotification);
}
+ int userId = entry.notification.getUserId();
boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
entry.notification) && !entry.row.isRemoved();
boolean childWithVisibleSummary = childNotification
&& mGroupManager.getGroupSummary(entry.notification).getVisibility()
== View.VISIBLE;
boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification);
- if (suppressedSummary || (isLockscreenPublicMode() && !mShowLockscreenNotifications) ||
- (onKeyguard && !childWithVisibleSummary
+ if (suppressedSummary
+ || (isLockscreenPublicMode(userId) && !mShowLockscreenNotifications)
+ || (onKeyguard && !childWithVisibleSummary
&& (visibleNotifications >= maxNotifications || !showOnKeyguard))) {
entry.row.setVisibility(View.GONE);
if (onKeyguard && showOnKeyguard && !childNotification && !suppressedSummary) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index b6e54af..bae16f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -380,7 +380,7 @@
return true;
}
- if (mEnvironment.onSecureLockScreen() &&
+ if (mEnvironment.isSecurelyLocked(sbn.getUserId()) &&
(sbn.getNotification().visibility == Notification.VISIBILITY_SECRET
|| mEnvironment.shouldHideNotifications(sbn.getUserId())
|| mEnvironment.shouldHideNotifications(sbn.getKey()))) {
@@ -463,7 +463,7 @@
* Provides access to keyguard state and user settings dependent data.
*/
public interface Environment {
- public boolean onSecureLockScreen();
+ public boolean isSecurelyLocked(int userId);
public boolean shouldHideNotifications(int userid);
public boolean shouldHideNotifications(String key);
public boolean isDeviceProvisioned();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
index 59e4244..f46fc67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.widget.ImageView;
import android.widget.RelativeLayout;
import com.android.keyguard.AlphaOptimizedImageButton;
@@ -41,6 +42,7 @@
public void onFinishInflate() {
super.onFinishInflate();
mIcon = (AlphaOptimizedImageButton) findViewById(R.id.car_nav_button_icon);
+ mIcon.setScaleType(ImageView.ScaleType.CENTER);
mIcon.setClickable(false);
mIcon.setBackgroundColor(android.R.color.transparent);
mIcon.setAlpha(UNSELECTED_ALPHA);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 706abdc..34b8371 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -96,9 +96,10 @@
}
final int activeUserId = ActivityManager.getCurrentUser();
- final boolean allowDismissKeyguard =
- !UserManager.isSplitSystemUser()
- && activeUserId == keyguardUserId;
+ final boolean isSystemUser =
+ UserManager.isSplitSystemUser() && activeUserId == UserHandle.USER_SYSTEM;
+ final boolean allowDismissKeyguard = !isSystemUser && activeUserId == keyguardUserId;
+
// If allowed, try to dismiss the Keyguard. If no security auth (password/pin/pattern) is
// set, this will dismiss the whole Keyguard. Otherwise, show the bouncer.
if (allowDismissKeyguard && mKeyguardView.dismiss(activeUserId)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 8ee014c..a442fc0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -37,6 +37,7 @@
import android.app.ActivityManagerNative;
import android.app.ActivityOptions;
import android.app.IActivityManager;
+import android.app.KeyguardManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.StatusBarManager;
@@ -1765,18 +1766,21 @@
for (int i=0; i<N; i++) {
Entry ent = activeNotifications.get(i);
int vis = ent.notification.getNotification().visibility;
+ int userId = ent.notification.getUserId();
// Display public version of the notification if we need to redact.
- final boolean hideSensitive =
- !userAllowsPrivateNotificationsInPublic(ent.notification.getUserId());
+ boolean deviceSensitive = (isLockscreenPublicMode(mCurrentUserId)
+ && !userAllowsPrivateNotificationsInPublic(mCurrentUserId));
+ boolean userSensitive = deviceSensitive || (isLockscreenPublicMode(userId)
+ && !userAllowsPrivateNotificationsInPublic(userId));
boolean sensitiveNote = vis == Notification.VISIBILITY_PRIVATE;
boolean sensitivePackage = packageHasVisibilityOverride(ent.notification.getKey());
- boolean sensitive = (sensitiveNote && hideSensitive) || sensitivePackage;
- boolean showingPublic = sensitive && isLockscreenPublicMode();
+ boolean sensitive = (sensitiveNote && userSensitive) || sensitivePackage;
+ boolean showingPublic = sensitive && isLockscreenPublicMode(userId);
if (showingPublic) {
updatePublicContentView(ent, ent.notification);
}
- ent.row.setSensitive(sensitive, hideSensitive);
+ ent.row.setSensitive(sensitive, deviceSensitive);
if (ent.autoRedacted && ent.legacy) {
// TODO: Also fade this? Or, maybe easier (and better), provide a dark redacted form
// for legacy auto redacted notifications.
@@ -4333,17 +4337,23 @@
}
private void updatePublicMode() {
- boolean isPublic = false;
- if (mStatusBarKeyguardViewManager.isShowing()) {
- for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
- UserInfo userInfo = mCurrentProfiles.valueAt(i);
- if (mStatusBarKeyguardViewManager.isSecure(userInfo.id)) {
- isPublic = true;
- break;
+ final boolean showingKeyguard = mStatusBarKeyguardViewManager.isShowing();
+ final boolean devicePublic = showingKeyguard
+ && mStatusBarKeyguardViewManager.isSecure(mCurrentUserId);
+
+ // Look for public mode users. Users are considered public in either case of:
+ // - device keyguard is shown in secure mode;
+ // - profile is locked with a work challenge.
+ for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
+ final int userId = mCurrentProfiles.valueAt(i).id;
+ boolean isProfilePublic = devicePublic;
+ if (!devicePublic && userId != mCurrentUserId) {
+ if (mStatusBarKeyguardViewManager.isSecure(userId)) {
+ isProfilePublic = mKeyguardManager.isDeviceLocked(userId);
}
}
+ setLockscreenPublicMode(isProfilePublic, userId);
}
- setLockscreenPublicMode(isPublic);
}
protected void updateKeyguardState(boolean goingToFullShade, boolean fromShadeLocked) {
@@ -4400,7 +4410,8 @@
public void updateStackScrollerState(boolean goingToFullShade, boolean fromShadeLocked) {
if (mStackScroller == null) return;
boolean onKeyguard = mState == StatusBarState.KEYGUARD;
- mStackScroller.setHideSensitive(isLockscreenPublicMode(), goingToFullShade);
+ boolean publicMode = isAnyProfilePublicMode();
+ mStackScroller.setHideSensitive(publicMode, goingToFullShade);
mStackScroller.setDimmed(onKeyguard, fromShadeLocked /* animate */);
mStackScroller.setExpandingEnabled(!onKeyguard);
ActivatableNotificationView activatedChild = mStackScroller.getActivatedChild();
@@ -4650,6 +4661,7 @@
* @param expandView The view to expand after going to the shade.
*/
public void goToLockedShade(View expandView) {
+ int userId = mCurrentUserId;
ExpandableNotificationRow row = null;
if (expandView instanceof ExpandableNotificationRow) {
row = (ExpandableNotificationRow) expandView;
@@ -4657,10 +4669,13 @@
// Indicate that the group expansion is changing at this time -- this way the group
// and children backgrounds / divider animations will look correct.
row.setGroupExpansionChanging(true);
+ if (row.getStatusBarNotification() != null) {
+ userId = row.getStatusBarNotification().getUserId();
+ }
}
boolean fullShadeNeedsBouncer = !userAllowsPrivateNotificationsInPublic(mCurrentUserId)
|| !mShowLockscreenNotifications || mFalsingManager.shouldEnforceBouncer();
- if (isLockscreenPublicMode() && fullShadeNeedsBouncer) {
+ if (isLockscreenPublicMode(userId) && fullShadeNeedsBouncer) {
mLeaveOpenOnKeyguardHide = true;
showBouncer();
mDraggedDownRow = row;
@@ -4705,10 +4720,20 @@
mPendingWorkRemoteInputView = clicked;
}
+ private boolean isAnyProfilePublicMode() {
+ for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
+ if (isLockscreenPublicMode(mCurrentProfiles.valueAt(i).id)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
@Override
- protected void onWorkChallengeUnlocked() {
- if (mPendingWorkRemoteInputView != null) {
- final View pendingWorkRemoteInputView = mPendingWorkRemoteInputView;
+ protected void onWorkChallengeChanged() {
+ updatePublicMode();
+ updateNotifications();
+ if (mPendingWorkRemoteInputView != null && !isAnyProfilePublicMode()) {
// Expand notification panel and the notification row, then click on remote input view
final Runnable clickPendingViewRunnable = new Runnable() {
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java b/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java
new file mode 100644
index 0000000..5cb5e68
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyFloat;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BatteryMeterDrawableTest {
+
+ private Context mContext;
+ private Resources mResources;
+ private BatteryMeterDrawable mBatteryMeter;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mResources = mContext.getResources();
+ mBatteryMeter = new BatteryMeterDrawable(mContext, 0);
+ }
+
+ @Test
+ public void testGetIntrinsicSize() {
+ assertEquals(
+ mResources.getDimensionPixelSize(R.dimen.battery_width),
+ mBatteryMeter.getIntrinsicWidth());
+ assertEquals(
+ mResources.getDimensionPixelSize(R.dimen.battery_height),
+ mBatteryMeter.getIntrinsicHeight());
+ }
+
+ @Test
+ public void testDrawNothingBeforeOnBatteryLevelChanged() {
+ final Canvas canvas = mock(Canvas.class);
+ mBatteryMeter.draw(canvas);
+ verify(canvas, never()).drawPath(any(), any());
+ verify(canvas, never()).drawText(anyString(), anyFloat(), anyFloat(), any());
+ }
+
+ @Test
+ public void testDrawImageButNoTextIfPluggedIn() {
+ mBatteryMeter.onBatteryLevelChanged(0, true, true);
+ final Canvas canvas = mock(Canvas.class);
+ mBatteryMeter.draw(canvas);
+ verify(canvas, atLeastOnce()).drawPath(any(), any());
+ verify(canvas, never()).drawText(anyString(), anyFloat(), anyFloat(), any());
+ }
+
+ @Test
+ public void testDrawTextIfNotPluggedIn() {
+ mBatteryMeter.onBatteryLevelChanged(0, false, false);
+ final Canvas canvas = mock(Canvas.class);
+ mBatteryMeter.draw(canvas);
+ verify(canvas, times(1)).drawText(anyString(), anyFloat(), anyFloat(), any());
+ }
+
+ @Test
+ public void testDrawNoTextIfPowerSaveEnabled() {
+ mBatteryMeter.onBatteryLevelChanged(0, false, false);
+ mBatteryMeter.onPowerSaveChanged(true);
+ final Canvas canvas = mock(Canvas.class);
+ mBatteryMeter.draw(canvas);
+ verify(canvas, never()).drawText(anyString(), anyFloat(), anyFloat(), any());
+ }
+
+ @Test
+ public void testDrawTextWarningAtCriticalLevel() {
+ int criticalLevel = mResources.getInteger(
+ com.android.internal.R.integer.config_criticalBatteryWarningLevel);
+ mBatteryMeter.onBatteryLevelChanged(criticalLevel, false, false);
+ final Canvas canvas = mock(Canvas.class);
+ mBatteryMeter.draw(canvas);
+ String warningString = mResources.getString(R.string.battery_meter_very_low_overlay_symbol);
+ verify(canvas, times(1)).drawText(eq(warningString), anyFloat(), anyFloat(), any());
+ }
+
+ @Test
+ public void testDrawTextNoWarningAboveCriticalLevel() {
+ int criticalLevel = mResources.getInteger(
+ com.android.internal.R.integer.config_criticalBatteryWarningLevel);
+ mBatteryMeter.onBatteryLevelChanged(criticalLevel + 1, false, false);
+ final Canvas canvas = mock(Canvas.class);
+ mBatteryMeter.draw(canvas);
+ String warningString = mResources.getString(R.string.battery_meter_very_low_overlay_symbol);
+ verify(canvas, never()).drawText(eq(warningString), anyFloat(), anyFloat(), any());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java
index ab7de39..9050b83 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java
@@ -33,13 +33,11 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.net.Uri;
import android.os.HandlerThread;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.PluginInstanceManager.ClassLoaderFactory;
import org.junit.After;
import org.junit.Before;
@@ -64,6 +62,7 @@
private PackageManager mMockPm;
private PluginListener mMockListener;
private PluginInstanceManager mPluginInstanceManager;
+ private PluginManager mMockManager;
@Before
public void setup() throws Exception {
@@ -72,9 +71,11 @@
mContextWrapper = new MyContextWrapper(getContext());
mMockPm = mock(PackageManager.class);
mMockListener = mock(PluginListener.class);
+ mMockManager = mock(PluginManager.class);
+ when(mMockManager.getClassLoader(Mockito.any(), Mockito.any()))
+ .thenReturn(getClass().getClassLoader());
mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
- mMockListener, true, mHandlerThread.getLooper(), 1, true,
- new TestClassLoaderFactory());
+ mMockListener, true, mHandlerThread.getLooper(), 1, mMockManager, true);
sMockPlugin = mock(Plugin.class);
when(sMockPlugin.getVersion()).thenReturn(1);
}
@@ -89,7 +90,7 @@
public void testNoPlugins() {
when(mMockPm.queryIntentServices(Mockito.any(), Mockito.anyInt())).thenReturn(
Collections.emptyList());
- mPluginInstanceManager.startListening();
+ mPluginInstanceManager.loadAll();
waitForIdleSync(mPluginInstanceManager.mPluginHandler);
waitForIdleSync(mPluginInstanceManager.mMainHandler);
@@ -112,7 +113,7 @@
public void testPluginDestroy() {
createPlugin(); // Get into valid created state.
- mPluginInstanceManager.stopListening();
+ mPluginInstanceManager.destroy();
waitForIdleSync(mPluginInstanceManager.mPluginHandler);
waitForIdleSync(mPluginInstanceManager.mMainHandler);
@@ -127,7 +128,7 @@
setupFakePmQuery();
when(sMockPlugin.getVersion()).thenReturn(2);
- mPluginInstanceManager.startListening();
+ mPluginInstanceManager.loadAll();
waitForIdleSync(mPluginInstanceManager.mPluginHandler);
waitForIdleSync(mPluginInstanceManager.mMainHandler);
@@ -141,10 +142,7 @@
public void testReloadOnChange() {
createPlugin(); // Get into valid created state.
- // Send a package changed broadcast.
- Intent i = new Intent(Intent.ACTION_PACKAGE_CHANGED,
- Uri.fromParts("package", "com.android.systemui", null));
- mPluginInstanceManager.onReceive(mContextWrapper, i);
+ mPluginInstanceManager.onPackageChange("com.android.systemui");
waitForIdleSync(mPluginInstanceManager.mPluginHandler);
waitForIdleSync(mPluginInstanceManager.mMainHandler);
@@ -164,11 +162,10 @@
public void testNonDebuggable() {
// Create a version that thinks the build is not debuggable.
mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
- mMockListener, true, mHandlerThread.getLooper(), 1, false,
- new TestClassLoaderFactory());
+ mMockListener, true, mHandlerThread.getLooper(), 1, mMockManager, false);
setupFakePmQuery();
- mPluginInstanceManager.startListening();
+ mPluginInstanceManager.loadAll();
waitForIdleSync(mPluginInstanceManager.mPluginHandler);
waitForIdleSync(mPluginInstanceManager.mMainHandler);;
@@ -236,19 +233,12 @@
private void createPlugin() {
setupFakePmQuery();
- mPluginInstanceManager.startListening();
+ mPluginInstanceManager.loadAll();
waitForIdleSync(mPluginInstanceManager.mPluginHandler);
waitForIdleSync(mPluginInstanceManager.mMainHandler);
}
- private static class TestClassLoaderFactory extends ClassLoaderFactory {
- @Override
- public ClassLoader createClassLoader(String path, ClassLoader base) {
- return base;
- }
- }
-
// Real context with no registering/unregistering of receivers.
private static class MyContextWrapper extends ContextWrapper {
public MyContextWrapper(Context base) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
index 56e742a..4b1827d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
@@ -51,7 +51,7 @@
mMockFactory = mock(PluginInstanceManagerFactory.class);
mMockPluginInstance = mock(PluginInstanceManager.class);
when(mMockFactory.createPluginInstanceManager(Mockito.any(), Mockito.any(), Mockito.any(),
- Mockito.anyBoolean(), Mockito.any(), Mockito.anyInt()))
+ Mockito.anyBoolean(), Mockito.any(), Mockito.anyInt(), Mockito.any()))
.thenReturn(mMockPluginInstance);
mPluginManager = new PluginManager(getContext(), mMockFactory, true, mMockExceptionHandler);
resetExceptionHandler();
@@ -62,7 +62,7 @@
public void testAddListener() {
mPluginManager.addPluginListener("myAction", mMockListener, 1);
- verify(mMockPluginInstance).startListening();
+ verify(mMockPluginInstance).loadAll();
}
@Test
@@ -70,7 +70,7 @@
mPluginManager.addPluginListener("myAction", mMockListener, 1);
mPluginManager.removePluginListener(mMockListener);
- verify(mMockPluginInstance).stopListening();
+ verify(mMockPluginInstance).destroy();
}
@Test
@@ -80,7 +80,7 @@
resetExceptionHandler();
mPluginManager.addPluginListener("myAction", mMockListener, 1);
- verify(mMockPluginInstance, Mockito.never()).startListening();
+ verify(mMockPluginInstance, Mockito.never()).loadAll();
}
@Test
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index da041da..815978e 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -2566,6 +2566,12 @@
// OPEN: Settings > Wireless > Manage wireless plan dialog
DIALOG_MANAGE_MOBILE_PLAN = 609;
+ // ACTION: Logs network type of the device while provisioning
+ PROVISIONING_NETWORK_TYPE = 610;
+
+ // ACTION: Logs action which triggered provisioning.
+ PROVISIONING_ACTION = 611;
+
// ---- End O Constants, all O constants go above this line ----
// Add new aosp constants above this line.
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 0b83e66..54c9afe 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -498,20 +498,6 @@
}
@Override
- public void sendAccessibilityEvents(ParceledListSlice events, int userId) {
- List<AccessibilityEvent> a11yEvents = events.getList();
- // Grab the lock once for the entire batch
- synchronized (mLock) {
- int numEventsToProcess = Math.min(a11yEvents.size(),
- AccessibilityManager.MAX_A11Y_EVENTS_PER_SERVICE_CALL);
- for (int i = 0; i < numEventsToProcess; i++) {
- AccessibilityEvent event = a11yEvents.get(i);
- sendAccessibilityEvent(event, userId);
- }
- }
- }
-
- @Override
public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(int userId) {
synchronized (mLock) {
// We treat calls from a profile as if made by its parent as profiles
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 8f16504..e64aa16 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -77,6 +77,7 @@
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
import android.provider.Settings;
import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.PhoneStateListener;
@@ -1699,6 +1700,7 @@
return;
}
+ Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "inetd bandwidth");
try {
mConnector.execute("bandwidth", suffix + chain, uid);
if (enable) {
@@ -1708,6 +1710,8 @@
}
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
}
}
}
@@ -1730,6 +1734,7 @@
Log.w(TAG, "setDataSaverMode(): already " + mDataSaverMode);
return true;
}
+ Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "bandwidthEnableDataSaver");
try {
final boolean changed = mNetdService.bandwidthEnableDataSaver(enable);
if (changed) {
@@ -1741,6 +1746,8 @@
} catch (RemoteException e) {
Log.w(TAG, "setDataSaverMode(" + enable + "): netd command failed", e);
return false;
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
}
}
}
diff --git a/services/core/java/com/android/server/accounts/AccountsDb.java b/services/core/java/com/android/server/accounts/AccountsDb.java
index 1adcf34..cb594f6 100644
--- a/services/core/java/com/android/server/accounts/AccountsDb.java
+++ b/services/core/java/com/android/server/accounts/AccountsDb.java
@@ -913,6 +913,9 @@
) > 0;
}
+ /**
+ * Returns list of all grants as {@link Pair pairs} of account name and UID.
+ */
List<Pair<String, Integer>> findAllAccountGrants() {
SQLiteDatabase db = mDeDatabase.getReadableDatabase();
try (Cursor cursor = db.rawQuery(ACCOUNT_ACCESS_GRANTS, null)) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c32cac8..9f2d447 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1136,6 +1136,20 @@
*/
private Configuration mTempGlobalConfig = new Configuration();
+ private final UpdateConfigurationResult mTmpUpdateConfigurationResult =
+ new UpdateConfigurationResult();
+ private static final class UpdateConfigurationResult {
+ // Configuration changes that were updated.
+ int changes;
+ // If the activity was relaunched to match the new configuration.
+ boolean activityRelaunched;
+
+ void reset() {
+ changes = 0;
+ activityRelaunched = false;
+ }
+ }
+
boolean mSuppressResizeConfigChanges;
/**
@@ -18872,7 +18886,7 @@
}
@Override
- public void updateConfiguration(Configuration values) {
+ public boolean updateConfiguration(Configuration values) {
enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
"updateConfiguration()");
@@ -18887,11 +18901,18 @@
}
final long origId = Binder.clearCallingIdentity();
- if (values != null) {
- Settings.System.clearConfiguration(values);
+
+ try {
+ if (values != null) {
+ Settings.System.clearConfiguration(values);
+ }
+ updateConfigurationLocked(values, null, false, false /* persistent */,
+ UserHandle.USER_NULL, false /* deferResume */,
+ mTmpUpdateConfigurationResult);
+ return mTmpUpdateConfigurationResult.changes != 0;
+ } finally {
+ Binder.restoreCallingIdentity(origId);
}
- updateConfigurationLocked(values, null, false);
- Binder.restoreCallingIdentity(origId);
}
}
@@ -18919,6 +18940,12 @@
// To cache the list of supported system locales
private String[] mSupportedSystemLocales = null;
+ private boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
+ boolean initLocale, boolean persistent, int userId, boolean deferResume) {
+ return updateConfigurationLocked(values, starting, initLocale, persistent, userId,
+ deferResume, null /* result */);
+ }
+
/**
* Do either or both things: (1) change the current configuration, and (2)
* make sure the given activity is running with the (now) current
@@ -18930,7 +18957,8 @@
* for that particular user
*/
private boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
- boolean initLocale, boolean persistent, int userId, boolean deferResume) {
+ boolean initLocale, boolean persistent, int userId, boolean deferResume,
+ UpdateConfigurationResult result) {
int changes = 0;
boolean kept = true;
@@ -18949,6 +18977,11 @@
mWindowManager.continueSurfaceLayout();
}
}
+
+ if (result != null) {
+ result.changes = changes;
+ result.activityRelaunched = !kept;
+ }
return kept;
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 547cc51..2bccfee 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -2046,25 +2046,30 @@
@Override
public void setRestrictBackground(boolean restrictBackground) {
- mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
- final long token = Binder.clearCallingIdentity();
+ Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "setRestrictBackground");
try {
- maybeRefreshTrustedTime();
- synchronized (mUidRulesFirstLock) {
- if (restrictBackground == mRestrictBackground) {
- // Ideally, UI should never allow this scenario...
- Slog.w(TAG, "setRestrictBackground: already " + restrictBackground);
- return;
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ maybeRefreshTrustedTime();
+ synchronized (mUidRulesFirstLock) {
+ if (restrictBackground == mRestrictBackground) {
+ // Ideally, UI should never allow this scenario...
+ Slog.w(TAG, "setRestrictBackground: already " + restrictBackground);
+ return;
+ }
+ setRestrictBackgroundUL(restrictBackground);
}
- setRestrictBackgroundUL(restrictBackground);
+
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
+ mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_CHANGED, restrictBackground ? 1 : 0, 0)
+ .sendToTarget();
} finally {
- Binder.restoreCallingIdentity(token);
+ Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
}
-
- mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_CHANGED, restrictBackground ? 1 : 0, 0)
- .sendToTarget();
}
private void setRestrictBackgroundUL(boolean restrictBackground) {
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
index ded85f3..00e45fd 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -150,6 +150,8 @@
private static final int MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS = 1;
+ private static final String ACTION_TRACK = "com.android.fitness.TRACK";
+
private final PackageManagerService mService;
private final Handler mHandler;
@@ -603,8 +605,9 @@
grantRuntimePermissionsLPw(musicPackage, STORAGE_PERMISSIONS, userId);
}
- // Android Wear Home
+ // Watches
if (mService.hasSystemFeature(PackageManager.FEATURE_WATCH, 0)) {
+ // Home application on watches
Intent homeIntent = new Intent(Intent.ACTION_MAIN);
homeIntent.addCategory(Intent.CATEGORY_HOME_MAIN);
@@ -621,6 +624,16 @@
grantRuntimePermissionsLPw(wearHomePackage, LOCATION_PERMISSIONS, false,
userId);
}
+
+ // Fitness tracking on watches
+ Intent trackIntent = new Intent(ACTION_TRACK);
+ PackageParser.Package trackPackage = getDefaultSystemHandlerActivityPackageLPr(
+ trackIntent, userId);
+ if (trackPackage != null
+ && doesPackageSupportRuntimePermissions(trackPackage)) {
+ grantRuntimePermissionsLPw(trackPackage, SENSORS_PERMISSIONS, false, userId);
+ grantRuntimePermissionsLPw(trackPackage, LOCATION_PERMISSIONS, false, userId);
+ }
}
// Print Spooler
diff --git a/services/core/java/com/android/server/policy/RecentApplicationsBackground.java b/services/core/java/com/android/server/policy/RecentApplicationsBackground.java
deleted file mode 100644
index 694a110..0000000
--- a/services/core/java/com/android/server/policy/RecentApplicationsBackground.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.policy;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.View;
-import android.widget.LinearLayout;
-
-/**
- * A vertical linear layout. However, instead of drawing the background
- * behnd the items, it draws the background outside the items based on the
- * padding. If there isn't enough room to draw both, it clips the background
- * instead of the contents.
- */
-public class RecentApplicationsBackground extends LinearLayout {
- private static final String TAG = "RecentApplicationsBackground";
-
- private boolean mBackgroundSizeChanged;
- private Drawable mBackground;
- private Rect mTmp0 = new Rect();
- private Rect mTmp1 = new Rect();
-
- public RecentApplicationsBackground(Context context) {
- this(context, null);
- init();
- }
-
- public RecentApplicationsBackground(Context context, AttributeSet attrs) {
- super(context, attrs);
- init();
- }
-
- private void init() {
- mBackground = getBackground();
- setBackgroundDrawable(null);
- setPadding(0, 0, 0, 0);
- setGravity(Gravity.CENTER);
- }
-
- @Override
- protected boolean setFrame(int left, int top, int right, int bottom) {
- setWillNotDraw(false);
- if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
- mBackgroundSizeChanged = true;
- }
- return super.setFrame(left, top, right, bottom);
- }
-
- @Override
- protected boolean verifyDrawable(Drawable who) {
- return who == mBackground || super.verifyDrawable(who);
- }
-
- @Override
- public void jumpDrawablesToCurrentState() {
- super.jumpDrawablesToCurrentState();
- if (mBackground != null) mBackground.jumpToCurrentState();
- }
-
- @Override
- protected void drawableStateChanged() {
- Drawable d = mBackground;
- if (d != null && d.isStateful()) {
- d.setState(getDrawableState());
- }
- super.drawableStateChanged();
- }
-
- @Override
- public void draw(Canvas canvas) {
- final Drawable background = mBackground;
- if (background != null) {
- if (mBackgroundSizeChanged) {
- mBackgroundSizeChanged = false;
- Rect chld = mTmp0;
- Rect bkg = mTmp1;
- mBackground.getPadding(bkg);
- getChildBounds(chld);
- // This doesn't clamp to this view's bounds, which is what we want,
- // so that the drawing is clipped.
- final int top = chld.top - bkg.top;
- final int bottom = chld.bottom + bkg.bottom;
- // The background here is a gradient that wants to
- // extend the full width of the screen (whatever that
- // may be).
- int left, right;
- if (false) {
- // This limits the width of the drawable.
- left = chld.left - bkg.left;
- right = chld.right + bkg.right;
- } else {
- // This expands it to full width.
- left = 0;
- right = getRight();
- }
- background.setBounds(left, top, right, bottom);
- }
- }
- mBackground.draw(canvas);
-
- if (false) {
- android.graphics.Paint p = new android.graphics.Paint();
- p.setColor(0x88ffff00);
- canvas.drawRect(background.getBounds(), p);
- }
- canvas.drawARGB((int)(0.75*0xff), 0, 0, 0);
-
- super.draw(canvas);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- mBackground.setCallback(this);
- setWillNotDraw(false);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- mBackground.setCallback(null);
- }
-
- private void getChildBounds(Rect r) {
- r.left = r.top = Integer.MAX_VALUE;
- r.bottom = r.right = Integer.MIN_VALUE;
- final int N = getChildCount();
- for (int i=0; i<N; i++) {
- View v = getChildAt(i);
- if (v.getVisibility() == View.VISIBLE) {
- r.left = Math.min(r.left, v.getLeft());
- r.top = Math.min(r.top, v.getTop());
- r.right = Math.max(r.right, v.getRight());
- r.bottom = Math.max(r.bottom, v.getBottom());
- }
- }
- }
-}
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index d219aed..353d663 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -872,6 +872,11 @@
} catch (RemoteException e) {
}
}
+ final Intent lockIntent = new Intent(Intent.ACTION_DEVICE_LOCKED_CHANGED);
+ lockIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ lockIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ mContext.sendBroadcastAsUser(lockIntent, UserHandle.SYSTEM,
+ Manifest.permission.TRUST_LISTENER, /* options */ null);
}
} finally {
Binder.restoreCallingIdentity(identity);
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 6075c12..508cf24 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -35,6 +35,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.NOTIFY_ACTIVITY_DRAWN;
+import static com.android.server.wm.WindowManagerService.H.NOTIFY_STARTING_WINDOW_DRAWN;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowManagerService.logWithStack;
@@ -91,19 +92,22 @@
// an activity have been drawn, so they can be made visible together
// at the same time.
// initialize so that it doesn't match mTransactionSequence which is an int.
- long lastTransactionSequence = Long.MIN_VALUE;
- int numInterestingWindows;
- int numDrawnWindows;
+ private long mLastTransactionSequence = Long.MIN_VALUE;
+ private int mNumInterestingWindows;
+ private int mNumDrawnWindows;
boolean inPendingTransaction;
boolean allDrawn;
// Set to true when this app creates a surface while in the middle of an animation. In that
// case do not clear allDrawn until the animation completes.
boolean deferClearAllDrawn;
- // These are to track the app's real drawing status if there were no saved surfaces.
+ /**
+ * These are to track the app's real drawing status if there were no saved surfaces.
+ * @see #updateDrawnWindowStates
+ */
boolean allDrawnExcludingSaved;
- int numInterestingWindowsExcludingSaved;
- int numDrawnWindowsExcludingSaved;
+ private int mNumInterestingWindowsExcludingSaved;
+ private int mNumDrawnWindowsExcludingSaved;
// Is this window's surface needed? This is almost like hidden, except
// it will sometimes be true a little earlier: when the token has
@@ -118,7 +122,7 @@
boolean reportedVisible;
// Last drawn state we reported to the app token.
- boolean reportedDrawn;
+ private boolean reportedDrawn;
// Set to true when the token has been removed from the window mgr.
boolean removed;
@@ -146,7 +150,7 @@
boolean mAppStopped;
int mRotationAnimationHint;
- int mPendingRelaunchCount;
+ private int mPendingRelaunchCount;
private ArrayList<WindowSurfaceController.SurfaceControlWithBackground> mSurfaceViewBackgrounds =
new ArrayList<>();
@@ -364,11 +368,9 @@
@Override
boolean isVisible() {
- if (hidden) {
- // TODO: Should this be checking hiddenRequested instead of hidden?
- return false;
- }
- return super.isVisible();
+ // If the app token isn't hidden then it is considered visible and there is no need to check
+ // its children windows to see if they are visible.
+ return !hidden;
}
@Override
@@ -1040,7 +1042,7 @@
stopFreezingScreen(false, true);
if (DEBUG_ORIENTATION) Slog.i(TAG,
"Setting mOrientationChangeComplete=true because wtoken " + this
- + " numInteresting=" + numInterestingWindows + " numDrawn=" + numDrawnWindows);
+ + " numInteresting=" + mNumInterestingWindows + " numDrawn=" + mNumDrawnWindows);
// This will set mOrientationChangeComplete and cause a pass through layout.
setAppLayoutChanges(FINISH_LAYOUT_REDO_WALLPAPER,
"checkAppWindowsReadyToShow: freezingScreen", displayId);
@@ -1054,30 +1056,28 @@
}
}
- @Override
- void updateAllDrawn(int displayId) {
- final DisplayContent displayContent = mService.mRoot.getDisplayContentOrCreate(displayId);
-
+ void updateAllDrawn(DisplayContent dc) {
if (!allDrawn) {
- final int numInteresting = numInterestingWindows;
- if (numInteresting > 0 && numDrawnWindows >= numInteresting) {
+ final int numInteresting = mNumInterestingWindows;
+ if (numInteresting > 0 && mNumDrawnWindows >= numInteresting) {
if (DEBUG_VISIBILITY) Slog.v(TAG, "allDrawn: " + this
- + " interesting=" + numInteresting + " drawn=" + numDrawnWindows);
+ + " interesting=" + numInteresting + " drawn=" + mNumDrawnWindows);
allDrawn = true;
// Force an additional layout pass where
// WindowStateAnimator#commitFinishDrawingLocked() will call performShowLocked().
- displayContent.setLayoutNeeded();
+ dc.setLayoutNeeded();
mService.mH.obtainMessage(NOTIFY_ACTIVITY_DRAWN, token).sendToTarget();
}
}
+
if (!allDrawnExcludingSaved) {
- int numInteresting = numInterestingWindowsExcludingSaved;
- if (numInteresting > 0 && numDrawnWindowsExcludingSaved >= numInteresting) {
+ int numInteresting = mNumInterestingWindowsExcludingSaved;
+ if (numInteresting > 0 && mNumDrawnWindowsExcludingSaved >= numInteresting) {
if (DEBUG_VISIBILITY) Slog.v(TAG, "allDrawnExcludingSaved: " + this
+ " interesting=" + numInteresting
- + " drawn=" + numDrawnWindowsExcludingSaved);
+ + " drawn=" + mNumDrawnWindowsExcludingSaved);
allDrawnExcludingSaved = true;
- displayContent.setLayoutNeeded();
+ dc.setLayoutNeeded();
if (isAnimatingInvisibleWithSavedSurface()
&& !mService.mFinishedEarlyAnim.contains(this)) {
mService.mFinishedEarlyAnim.add(this);
@@ -1086,6 +1086,87 @@
}
}
+ /**
+ * Updated this app token tracking states for interesting and drawn windows based on the window.
+ *
+ * @return Returns true if the input window is considered interesting and drawn while all the
+ * windows in this app token where not considered drawn as of the last pass.
+ */
+ boolean updateDrawnWindowStates(WindowState w) {
+ if (DEBUG_STARTING_WINDOW && w == startingWindow) {
+ Slog.d(TAG, "updateWindows: starting " + w + " isOnScreen=" + w.isOnScreen()
+ + " allDrawn=" + allDrawn + " freezingScreen=" + mAppAnimator.freezingScreen);
+ }
+
+ if (allDrawn && allDrawnExcludingSaved && !mAppAnimator.freezingScreen) {
+ return false;
+ }
+
+ if (mLastTransactionSequence != mService.mTransactionSequence) {
+ mLastTransactionSequence = mService.mTransactionSequence;
+ mNumInterestingWindows = mNumDrawnWindows = 0;
+ mNumInterestingWindowsExcludingSaved = 0;
+ mNumDrawnWindowsExcludingSaved = 0;
+ startingDisplayed = false;
+ }
+
+ final WindowStateAnimator winAnimator = w.mWinAnimator;
+
+ boolean isInterestingAndDrawn = false;
+
+ if (!allDrawn && w.mightAffectAllDrawn(false /* visibleOnly */)) {
+ if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) {
+ Slog.v(TAG, "Eval win " + w + ": isDrawn=" + w.isDrawnLw()
+ + ", isAnimationSet=" + winAnimator.isAnimationSet());
+ if (!w.isDrawnLw()) {
+ Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurfaceController
+ + " pv=" + w.mPolicyVisibility
+ + " mDrawState=" + winAnimator.drawStateToString()
+ + " ph=" + w.isParentWindowHidden() + " th=" + hiddenRequested
+ + " a=" + winAnimator.mAnimating);
+ }
+ }
+
+ if (w != startingWindow) {
+ if (w.isInteresting()) {
+ mNumInterestingWindows++;
+ if (w.isDrawnLw()) {
+ mNumDrawnWindows++;
+
+ if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) Slog.v(TAG, "tokenMayBeDrawn: "
+ + this + " w=" + w + " numInteresting=" + mNumInterestingWindows
+ + " freezingScreen=" + mAppAnimator.freezingScreen
+ + " mAppFreezing=" + w.mAppFreezing);
+
+ isInterestingAndDrawn = true;
+ }
+ }
+ } else if (w.isDrawnLw()) {
+ mService.mH.sendEmptyMessage(NOTIFY_STARTING_WINDOW_DRAWN);
+ startingDisplayed = true;
+ }
+ }
+
+ if (!allDrawnExcludingSaved && w.mightAffectAllDrawn(true /* visibleOnly */)) {
+ if (w != startingWindow && w.isInteresting()) {
+ mNumInterestingWindowsExcludingSaved++;
+ if (w.isDrawnLw() && !w.isAnimatingWithSavedSurface()) {
+ mNumDrawnWindowsExcludingSaved++;
+
+ if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) Slog.v(TAG,
+ "tokenMayBeDrawnExcludingSaved: " + this + " w=" + w
+ + " numInteresting=" + mNumInterestingWindowsExcludingSaved
+ + " freezingScreen=" + mAppAnimator.freezingScreen
+ + " mAppFreezing=" + w.mAppFreezing);
+
+ isInterestingAndDrawn = true;
+ }
+ }
+ }
+
+ return isInterestingAndDrawn;
+ }
+
@Override
void stepAppWindowsAnimation(long currentTime, int displayId) {
mAppAnimator.wasAnimating = mAppAnimator.animating;
@@ -1147,11 +1228,11 @@
if (mAppStopped) {
pw.print(prefix); pw.print("mAppStopped="); pw.println(mAppStopped);
}
- if (numInterestingWindows != 0 || numDrawnWindows != 0
+ if (mNumInterestingWindows != 0 || mNumDrawnWindows != 0
|| allDrawn || mAppAnimator.allDrawn) {
- pw.print(prefix); pw.print("numInterestingWindows=");
- pw.print(numInterestingWindows);
- pw.print(" numDrawnWindows="); pw.print(numDrawnWindows);
+ pw.print(prefix); pw.print("mNumInterestingWindows=");
+ pw.print(mNumInterestingWindows);
+ pw.print(" mNumDrawnWindows="); pw.print(mNumDrawnWindows);
pw.print(" inPendingTransaction="); pw.print(inPendingTransaction);
pw.print(" allDrawn="); pw.print(allDrawn);
pw.print(" (animator="); pw.print(mAppAnimator.allDrawn);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 34a7390..42e9e23 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -240,10 +240,6 @@
super.checkAppWindowsReadyToShow(mDisplayId);
}
- void updateAllDrawn() {
- super.updateAllDrawn(mDisplayId);
- }
-
void stepAppWindowsAnimation(long currentTime) {
super.stepAppWindowsAnimation(currentTime, mDisplayId);
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 219fd8e..1700bca 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -41,12 +41,12 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.LinkedList;
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE;
import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
@@ -65,7 +65,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_POWER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
@@ -76,7 +75,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_KEEP_SCREEN_ON;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.H.NOTIFY_STARTING_WINDOW_DRAWN;
import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS;
import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
@@ -136,6 +134,8 @@
private final ArrayList<Integer> mChangedStackList = new ArrayList();
+ private final LinkedList<AppWindowToken> mTmpUpdateAllDrawn = new LinkedList();
+
// State for the RemoteSurfaceTrace system used in testing. If this is enabled SurfaceControl
// instances will be replaced with an instance that writes a binary representation of all
// commands to mSurfaceTraceFd.
@@ -707,7 +707,7 @@
">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
mService.openSurfaceTransaction();
try {
- mService.mRoot.applySurfaceChangesTransaction(recoveringMemory, defaultDw, defaultDh);
+ applySurfaceChangesTransaction(recoveringMemory, defaultDw, defaultDh);
} catch (RuntimeException e) {
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
@@ -990,7 +990,6 @@
final int count = mChildren.size();
for (int j = 0; j < count; ++j) {
final DisplayContent dc = mChildren.get(j);
- boolean updateAllDrawn = false;
WindowList windows = dc.getWindowList();
DisplayInfo displayInfo = dc.getDisplayInfo();
final int displayId = dc.getDisplayId();
@@ -1003,6 +1002,7 @@
mDisplayHasContent = false;
mPreferredRefreshRate = 0;
mPreferredModeId = 0;
+ mTmpUpdateAllDrawn.clear();
int repeats = 0;
do {
@@ -1086,45 +1086,14 @@
if (isDefaultDisplay && obscuredChanged && w.isVisibleLw()
&& mService.mWallpaperControllerLocked.isWallpaperTarget(w)) {
// This is the wallpaper target and its obscured state changed... make sure the
- // current wallaper's visibility has been updated accordingly.
+ // current wallpaper's visibility has been updated accordingly.
mService.mWallpaperControllerLocked.updateWallpaperVisibility();
}
+ w.handleWindowMovedIfNeeded();
+
final WindowStateAnimator winAnimator = w.mWinAnimator;
- // If the window has moved due to its containing content frame changing, then
- // notify the listeners and optionally animate it. Simply checking a change of
- // position is not enough, because being move due to dock divider is not a trigger
- // for animation.
- if (w.hasMoved()) {
- // Frame has moved, containing content frame has also moved, and we're not
- // currently animating... let's do something.
- final int left = w.mFrame.left;
- final int top = w.mFrame.top;
- final boolean adjustedForMinimizedDockOrIme = task != null
- && (task.mStack.isAdjustedForMinimizedDockedStack()
- || task.mStack.isAdjustedForIme());
- if (mService.okToDisplay()
- && (w.mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
- && !w.isDragResizing() && !adjustedForMinimizedDockOrIme
- && (task == null || w.getTask().mStack.hasMovementAnimations())
- && !w.mWinAnimator.mLastHidden) {
- winAnimator.setMoveAnimation(left, top);
- }
-
- //TODO (multidisplay): Accessibility supported only for the default display.
- if (mService.mAccessibilityController != null
- && displayId == Display.DEFAULT_DISPLAY) {
- mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
- }
-
- try {
- w.mClient.moved(left, top);
- } catch (RemoteException e) {
- }
- w.mMovedByResize = false;
- }
-
//Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
w.mContentChanged = false;
@@ -1171,71 +1140,10 @@
}
final AppWindowToken atoken = w.mAppToken;
- if (DEBUG_STARTING_WINDOW && atoken != null && w == atoken.startingWindow) {
- Slog.d(TAG, "updateWindows: starting " + w
- + " isOnScreen=" + w.isOnScreen() + " allDrawn=" + atoken.allDrawn
- + " freezingScreen=" + atoken.mAppAnimator.freezingScreen);
- }
- if (atoken != null && (!atoken.allDrawn || !atoken.allDrawnExcludingSaved
- || atoken.mAppAnimator.freezingScreen)) {
- if (atoken.lastTransactionSequence != mService.mTransactionSequence) {
- atoken.lastTransactionSequence = mService.mTransactionSequence;
- atoken.numInterestingWindows = atoken.numDrawnWindows = 0;
- atoken.numInterestingWindowsExcludingSaved = 0;
- atoken.numDrawnWindowsExcludingSaved = 0;
- atoken.startingDisplayed = false;
- }
- if (!atoken.allDrawn && w.mightAffectAllDrawn(false /* visibleOnly */)) {
- if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) {
- Slog.v(TAG, "Eval win " + w + ": isDrawn="
- + w.isDrawnLw()
- + ", isAnimationSet=" + winAnimator.isAnimationSet());
- if (!w.isDrawnLw()) {
- Slog.v(TAG, "Not displayed: s="
- + winAnimator.mSurfaceController
- + " pv=" + w.mPolicyVisibility
- + " mDrawState=" + winAnimator.drawStateToString()
- + " ph=" + w.isParentWindowHidden()
- + " th=" + atoken.hiddenRequested
- + " a=" + winAnimator.mAnimating);
- }
- }
- if (w != atoken.startingWindow) {
- if (w.isInteresting()) {
- atoken.numInterestingWindows++;
- if (w.isDrawnLw()) {
- atoken.numDrawnWindows++;
- if (DEBUG_VISIBILITY || DEBUG_ORIENTATION)
- Slog.v(TAG, "tokenMayBeDrawn: " + atoken
- + " w=" + w + " numInteresting="
- + atoken.numInterestingWindows
- + " freezingScreen="
- + atoken.mAppAnimator.freezingScreen
- + " mAppFreezing=" + w.mAppFreezing);
- updateAllDrawn = true;
- }
- }
- } else if (w.isDrawnLw()) {
- mService.mH.sendEmptyMessage(NOTIFY_STARTING_WINDOW_DRAWN);
- atoken.startingDisplayed = true;
- }
- }
- if (!atoken.allDrawnExcludingSaved
- && w.mightAffectAllDrawn(true /* visibleOnly */)) {
- if (w != atoken.startingWindow && w.isInteresting()) {
- atoken.numInterestingWindowsExcludingSaved++;
- if (w.isDrawnLw() && !w.isAnimatingWithSavedSurface()) {
- atoken.numDrawnWindowsExcludingSaved++;
- if (DEBUG_VISIBILITY || DEBUG_ORIENTATION)
- Slog.v(TAG, "tokenMayBeDrawnExcludingSaved: " + atoken
- + " w=" + w + " numInteresting="
- + atoken.numInterestingWindowsExcludingSaved
- + " freezingScreen="
- + atoken.mAppAnimator.freezingScreen
- + " mAppFreezing=" + w.mAppFreezing);
- updateAllDrawn = true;
- }
- }
+ if (atoken != null) {
+ final boolean updateAllDrawn = atoken.updateDrawnWindowStates(w);
+ if (updateAllDrawn && !mTmpUpdateAllDrawn.contains(atoken)) {
+ mTmpUpdateAllDrawn.add(atoken);
}
}
@@ -1244,7 +1152,7 @@
focusDisplayed = true;
}
- mService.updateResizingWindows(w);
+ w.updateResizingWindowIfNeeded();
}
mService.mDisplayManagerInternal.setDisplayProperties(displayId,
@@ -1255,10 +1163,11 @@
dc.stopDimmingIfNeeded();
- if (updateAllDrawn) {
+ while (!mTmpUpdateAllDrawn.isEmpty()) {
+ final AppWindowToken atoken = mTmpUpdateAllDrawn.removeLast();
// See if any windows have been drawn, so they (and others associated with them)
// can now be shown.
- dc.updateAllDrawn();
+ atoken.updateAllDrawn(dc);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 8777d88..902f4ae 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -381,19 +381,6 @@
}
}
- /**
- * Updates the current all drawn status for this container. That is all its children
- * that should draw something have done so.
- */
- // TODO: The displayId shouldn't be needed as there shouldn't be a container on more than one
- // display. Remove once we migrate DisplayContent to use WindowContainer.
- void updateAllDrawn(int displayId) {
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- final WindowContainer wc = mChildren.get(i);
- wc.updateAllDrawn(displayId);
- }
- }
-
/** Step currently ongoing animation for App window containers. */
// TODO: The displayId shouldn't be needed as there shouldn't be a container on more than one
// display. Remove once we migrate DisplayContent to use WindowContainer.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 310ad5a..33155ce 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -213,7 +213,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
@@ -222,12 +221,10 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEEP_SCREEN_ON;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RESIZE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREEN_ON;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
@@ -5441,25 +5438,30 @@
}
}
- public void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {
- if(DEBUG_ORIENTATION) Slog.v(TAG_WM, "updateRotationUnchecked("
- + "alwaysSendConfiguration=" + alwaysSendConfiguration + ")");
+ private void updateRotationUnchecked(boolean alwaysSendConfiguration,
+ boolean forceRelayout) {
+ if(DEBUG_ORIENTATION) Slog.v(TAG_WM, "updateRotationUnchecked:"
+ + " alwaysSendConfiguration=" + alwaysSendConfiguration
+ + " forceRelayout=" + forceRelayout);
long origId = Binder.clearCallingIdentity();
- boolean changed;
- synchronized(mWindowMap) {
- changed = updateRotationUncheckedLocked(false);
- if (!changed || forceRelayout) {
- getDefaultDisplayContentLocked().setLayoutNeeded();
- mWindowPlacerLocked.performSurfacePlacement();
+
+ try {
+ final boolean rotationChanged;
+ synchronized (mWindowMap) {
+ rotationChanged = updateRotationUncheckedLocked(false);
+ if (!rotationChanged || forceRelayout) {
+ getDefaultDisplayContentLocked().setLayoutNeeded();
+ mWindowPlacerLocked.performSurfacePlacement();
+ }
}
- }
- if (changed || alwaysSendConfiguration) {
- sendNewConfiguration();
+ if (rotationChanged || alwaysSendConfiguration) {
+ sendNewConfiguration();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
}
-
- Binder.restoreCallingIdentity(origId);
}
@@ -5470,7 +5472,7 @@
* Returns true if the rotation has been changed. In this case YOU
* MUST CALL sendNewConfiguration() TO UNFREEZE THE SCREEN.
*/
- public boolean updateRotationUncheckedLocked(boolean inTransaction) {
+ boolean updateRotationUncheckedLocked(boolean inTransaction) {
if (mDeferredRotationPauseCount > 0) {
// Rotation updates have been paused temporarily. Defer the update until
// updates have been resumed.
@@ -6121,10 +6123,25 @@
/**
* Instruct the Activity Manager to fetch new configurations, update global configuration
* and broadcast changes to config-changed listeners if appropriate.
+ * NOTE: Can't be called with the window manager lock held since it call into activity manager.
*/
void sendNewConfiguration() {
try {
- mActivityManager.updateConfiguration(null);
+ final boolean configUpdated = mActivityManager.updateConfiguration(null);
+ if (!configUpdated) {
+ // Something changed (E.g. device rotation), but no configuration update is needed.
+ // E.g. changing device rotation by 180 degrees. Go ahead and perform surface
+ // placement to unfreeze the display since we froze it when the rotation was updated
+ // in updateRotationUncheckedLocked.
+ synchronized (mWindowMap) {
+ if (mWaitingForConfig) {
+ mWaitingForConfig = false;
+ mLastFinishedFreezeSource = "config-unchanged";
+ getDefaultDisplayContentLocked().setLayoutNeeded();
+ mWindowPlacerLocked.performSurfacePlacement();
+ }
+ }
+ }
} catch (RemoteException e) {
}
}
@@ -8048,108 +8065,6 @@
return changes;
}
- void updateResizingWindows(final WindowState w) {
- final WindowStateAnimator winAnimator = w.mWinAnimator;
- if (w.mHasSurface && w.mLayoutSeq == mLayoutSeq && !w.isGoneForLayoutLw()) {
- final Task task = w.getTask();
- // In the case of stack bound animations, the window frames will update (unlike other
- // animations which just modify various transformation properties). We don't want to
- // notify the client of frame changes in this case. Not only is it a lot of churn, but
- // the frame may not correspond to the surface size or the onscreen area at various
- // phases in the animation, and the client will become sad and confused.
- if (task != null && task.mStack.getBoundsAnimating()) {
- return;
- }
- w.setReportResizeHints();
- boolean configChanged = w.isConfigChanged();
- if (DEBUG_CONFIGURATION && configChanged) {
- Slog.v(TAG_WM, "Win " + w + " config changed: " + w.getConfiguration());
- }
- final boolean dragResizingChanged = w.isDragResizeChanged()
- && !w.isDragResizingChangeReported();
-
- if (localLOGV) Slog.v(TAG_WM, "Resizing " + w + ": configChanged=" + configChanged
- + " dragResizingChanged=" + dragResizingChanged + " last=" + w.mLastFrame
- + " frame=" + w.mFrame);
-
- // We update mLastFrame always rather than in the conditional with the
- // last inset variables, because mFrameSizeChanged only tracks the
- // width and height changing.
- w.mLastFrame.set(w.mFrame);
-
- if (w.mContentInsetsChanged
- || w.mVisibleInsetsChanged
- || winAnimator.mSurfaceResized
- || w.mOutsetsChanged
- || w.mFrameSizeChanged
- || configChanged
- || dragResizingChanged
- || !w.isResizedWhileNotDragResizingReported()) {
- if (DEBUG_RESIZE || DEBUG_ORIENTATION) {
- Slog.v(TAG_WM, "Resize reasons for w=" + w + ": "
- + " contentInsetsChanged=" + w.mContentInsetsChanged
- + " " + w.mContentInsets.toShortString()
- + " visibleInsetsChanged=" + w.mVisibleInsetsChanged
- + " " + w.mVisibleInsets.toShortString()
- + " stableInsetsChanged=" + w.mStableInsetsChanged
- + " " + w.mStableInsets.toShortString()
- + " outsetsChanged=" + w.mOutsetsChanged
- + " " + w.mOutsets.toShortString()
- + " surfaceResized=" + winAnimator.mSurfaceResized
- + " configChanged=" + configChanged
- + " dragResizingChanged=" + dragResizingChanged
- + " resizedWhileNotDragResizingReported="
- + w.isResizedWhileNotDragResizingReported());
- }
-
- // If it's a dead window left on screen, and the configuration changed,
- // there is nothing we can do about it. Remove the window now.
- if (w.mAppToken != null && w.mAppDied) {
- w.mAppToken.removeDeadWindows();
- return;
- }
-
- w.mLastOverscanInsets.set(w.mOverscanInsets);
- w.mLastContentInsets.set(w.mContentInsets);
- w.mLastVisibleInsets.set(w.mVisibleInsets);
- w.mLastStableInsets.set(w.mStableInsets);
- w.mLastOutsets.set(w.mOutsets);
- makeWindowFreezingScreenIfNeededLocked(w);
- // If the orientation is changing, or we're starting or ending
- // a drag resizing action, then we need to hold off on unfreezing
- // the display until this window has been redrawn; to do that,
- // we need to go through the process of getting informed by the
- // application when it has finished drawing.
- if (w.mOrientationChanging || dragResizingChanged
- || w.isResizedWhileNotDragResizing()) {
- if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || DEBUG_ORIENTATION || DEBUG_RESIZE) {
- Slog.v(TAG_WM, "Orientation or resize start waiting for draw"
- + ", mDrawState=DRAW_PENDING in " + w
- + ", surfaceController " + winAnimator.mSurfaceController);
- }
- winAnimator.mDrawState = DRAW_PENDING;
- if (w.mAppToken != null) {
- w.mAppToken.clearAllDrawn();
- }
- }
- if (!mResizingWindows.contains(w)) {
- if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG_WM,
- "Resizing window " + w);
- mResizingWindows.add(w);
- }
- } else if (w.mOrientationChanging) {
- if (w.isDrawnLw()) {
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
- "Orientation not waiting for draw in "
- + w + ", surfaceController " + winAnimator.mSurfaceController);
- w.mOrientationChanging = false;
- w.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
- - mDisplayFreezeTime);
- }
- }
- }
- }
-
void checkDrawnWindowsLocked() {
if (mWaitingForDrawn.isEmpty() || mWaitingForDrawnCallback == null) {
return;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index ee2da13..435b1d2 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -87,6 +87,7 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
@@ -131,6 +132,7 @@
import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT;
import static com.android.server.wm.WindowManagerService.localLOGV;
import static com.android.server.wm.WindowStateAnimator.COMMIT_DRAW_PENDING;
+import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
@@ -224,14 +226,12 @@
int mLayoutSeq = -1;
- private final Configuration mTmpConfig = new Configuration();
- // Represents the changes from our override configuration applied
- // to the global configuration. This is the only form of configuration
- // which is suitable for delivery to the client.
- private Configuration mMergedConfiguration = new Configuration();
- // Sticky answer to isConfigChanged(), remains true until new Configuration is assigned.
- // Used only on {@link #TYPE_KEYGUARD}.
- private boolean mConfigHasChanged;
+ /**
+ * Used to store last reported to client configuration and check if we have newer available.
+ * We'll send configuration to client only if it is different from the last applied one and
+ * client won't perform unnecessary updates.
+ */
+ private final Configuration mLastReportedConfiguration = new Configuration();
/**
* Actual position of the surface shown on-screen (may be modified by animation). These are
@@ -1081,6 +1081,112 @@
|| mOutsetsChanged || mFrameSizeChanged;
}
+ /**
+ * Adds the window to the resizing list if any of the parameters we use to track the window
+ * dimensions or insets have changed.
+ */
+ void updateResizingWindowIfNeeded() {
+ final WindowStateAnimator winAnimator = mWinAnimator;
+ if (!mHasSurface || mService.mLayoutSeq != mLayoutSeq || isGoneForLayoutLw()) {
+ return;
+ }
+
+ final Task task = getTask();
+ // In the case of stack bound animations, the window frames will update (unlike other
+ // animations which just modify various transformation properties). We don't want to
+ // notify the client of frame changes in this case. Not only is it a lot of churn, but
+ // the frame may not correspond to the surface size or the onscreen area at various
+ // phases in the animation, and the client will become sad and confused.
+ if (task != null && task.mStack.getBoundsAnimating()) {
+ return;
+ }
+
+ setReportResizeHints();
+ boolean configChanged = isConfigChanged();
+ if (DEBUG_CONFIGURATION && configChanged) {
+ Slog.v(TAG_WM, "Win " + this + " config changed: " + getConfiguration());
+ }
+
+ final boolean dragResizingChanged = isDragResizeChanged()
+ && !isDragResizingChangeReported();
+
+ if (localLOGV) Slog.v(TAG_WM, "Resizing " + this + ": configChanged=" + configChanged
+ + " dragResizingChanged=" + dragResizingChanged + " last=" + mLastFrame
+ + " frame=" + mFrame);
+
+ // We update mLastFrame always rather than in the conditional with the last inset
+ // variables, because mFrameSizeChanged only tracks the width and height changing.
+ mLastFrame.set(mFrame);
+
+ if (mContentInsetsChanged
+ || mVisibleInsetsChanged
+ || winAnimator.mSurfaceResized
+ || mOutsetsChanged
+ || mFrameSizeChanged
+ || configChanged
+ || dragResizingChanged
+ || !isResizedWhileNotDragResizingReported()) {
+ if (DEBUG_RESIZE || DEBUG_ORIENTATION) {
+ Slog.v(TAG_WM, "Resize reasons for w=" + this + ": "
+ + " contentInsetsChanged=" + mContentInsetsChanged
+ + " " + mContentInsets.toShortString()
+ + " visibleInsetsChanged=" + mVisibleInsetsChanged
+ + " " + mVisibleInsets.toShortString()
+ + " stableInsetsChanged=" + mStableInsetsChanged
+ + " " + mStableInsets.toShortString()
+ + " outsetsChanged=" + mOutsetsChanged
+ + " " + mOutsets.toShortString()
+ + " surfaceResized=" + winAnimator.mSurfaceResized
+ + " configChanged=" + configChanged
+ + " dragResizingChanged=" + dragResizingChanged
+ + " resizedWhileNotDragResizingReported="
+ + isResizedWhileNotDragResizingReported());
+ }
+
+ // If it's a dead window left on screen, and the configuration changed, there is nothing
+ // we can do about it. Remove the window now.
+ if (mAppToken != null && mAppDied) {
+ mAppToken.removeDeadWindows();
+ return;
+ }
+
+ mLastOverscanInsets.set(mOverscanInsets);
+ mLastContentInsets.set(mContentInsets);
+ mLastVisibleInsets.set(mVisibleInsets);
+ mLastStableInsets.set(mStableInsets);
+ mLastOutsets.set(mOutsets);
+ mService.makeWindowFreezingScreenIfNeededLocked(this);
+
+ // If the orientation is changing, or we're starting or ending a drag resizing action,
+ // then we need to hold off on unfreezing the display until this window has been
+ // redrawn; to do that, we need to go through the process of getting informed by the
+ // application when it has finished drawing.
+ if (mOrientationChanging || dragResizingChanged || isResizedWhileNotDragResizing()) {
+ if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || DEBUG_ORIENTATION || DEBUG_RESIZE) {
+ Slog.v(TAG_WM, "Orientation or resize start waiting for draw"
+ + ", mDrawState=DRAW_PENDING in " + this
+ + ", surfaceController " + winAnimator.mSurfaceController);
+ }
+ winAnimator.mDrawState = DRAW_PENDING;
+ if (mAppToken != null) {
+ mAppToken.clearAllDrawn();
+ }
+ }
+ if (!mService.mResizingWindows.contains(this)) {
+ if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG_WM, "Resizing window " + this);
+ mService.mResizingWindows.add(this);
+ }
+ } else if (mOrientationChanging) {
+ if (isDrawnLw()) {
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Orientation not waiting for draw in "
+ + this + ", surfaceController " + winAnimator.mSurfaceController);
+ mOrientationChanging = false;
+ mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
+ - mService.mDisplayFreezeTime);
+ }
+ }
+ }
+
public DisplayContent getDisplayContent() {
if (mAppToken == null || mNotOnAppsDisplay) {
return mDisplayContent;
@@ -1194,7 +1300,22 @@
@Override
boolean isVisible() {
- if ((mAppToken == null || !mAppToken.hiddenRequested) && isVisibleUnchecked()) {
+ // TODO: The check for hiddenRequested is commented out below, because the window can still
+ // be visible on screen when the flag is true. We would like the isVisible() method to
+ // return an answer closer to if the window is truly visible (can't be an exact answer
+ // without checking the surface state), so comment out the check for now so we can test to
+ // see what problem it causes.
+ // If it doesn't cause any issues, then we can remove just before we lock down the current
+ // release (O) and also consolidate this method with #isVisibleUnchecked() and possibly
+ // other methods like isVisibleNow().
+ // If it does cause problems, then we can look if there are other ways to solve the problem.
+ // If there isn't then uncomment and document here why it is needed.
+ if (/*(mAppToken == null || !mAppToken.hiddenRequested) && */isVisibleUnchecked()
+ // TODO: The window isn't considered visible when the token is hidden, however
+ // uncommenting the check below breaks the visual transition from an app to the launcher
+ // if the home buttons is pressed. Need to investigate an fix that issue before
+ // uncommenting.
+ /* && !mToken.hidden*/) {
// Is this window visible? It is not visible if there is no surface, or we are in the
// process of running an exit animation that will remove the surface, or its app token
// has been hidden.
@@ -1426,7 +1547,7 @@
* Return true if the window is opaque and fully drawn. This indicates
* it may obscure windows behind it.
*/
- boolean isOpaqueDrawn() {
+ private boolean isOpaqueDrawn() {
// When there is keyguard, wallpaper could be placed over the secure app
// window but invisible. We need to check wallpaper visibility explicitly
// to determine if it's occluding apps.
@@ -1557,10 +1678,49 @@
}
/**
+ * If the window has moved due to its containing content frame changing, then notify the
+ * listeners and optionally animate it. Simply checking a change of position is not enough,
+ * because being move due to dock divider is not a trigger for animation.
+ */
+ void handleWindowMovedIfNeeded() {
+ if (!hasMoved()) {
+ return;
+ }
+
+ // Frame has moved, containing content frame has also moved, and we're not currently
+ // animating... let's do something.
+ final int left = mFrame.left;
+ final int top = mFrame.top;
+ final Task task = getTask();
+ final boolean adjustedForMinimizedDockOrIme = task != null
+ && (task.mStack.isAdjustedForMinimizedDockedStack()
+ || task.mStack.isAdjustedForIme());
+ if (mService.okToDisplay()
+ && (mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
+ && !isDragResizing() && !adjustedForMinimizedDockOrIme
+ && (task == null || getTask().mStack.hasMovementAnimations())
+ && !mWinAnimator.mLastHidden) {
+ mWinAnimator.setMoveAnimation(left, top);
+ }
+
+ //TODO (multidisplay): Accessibility supported only for the default display.
+ if (mService.mAccessibilityController != null
+ && getDisplayContent().getDisplayId() == Display.DEFAULT_DISPLAY) {
+ mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
+ }
+
+ try {
+ mClient.moved(left, top);
+ } catch (RemoteException e) {
+ }
+ mMovedByResize = false;
+ }
+
+ /**
* Return whether this window has moved. (Only makes
* sense to call from performLayoutAndPlaceSurfacesLockedInner().)
*/
- boolean hasMoved() {
+ private boolean hasMoved() {
return mHasSurface && (mContentChanged || mMovedByResize)
&& !mAnimatingExit
&& (mFrame.top != mLastFrame.top || mFrame.left != mLastFrame.left)
@@ -1583,21 +1743,9 @@
&& mFrame.right >= displayInfo.appWidth && mFrame.bottom >= displayInfo.appHeight;
}
+ /** Returns true if last applied config was not yet requested by client. */
boolean isConfigChanged() {
- getMergedConfig(mTmpConfig);
-
- // If the merged configuration is still empty, it means that we haven't issued the
- // configuration to the client yet and we need to return true so the configuration updates.
- boolean configChanged = mMergedConfiguration.equals(Configuration.EMPTY)
- || mTmpConfig.diff(mMergedConfiguration) != 0;
-
- if ((mAttrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
- // Retain configuration changed status until resetConfiguration called.
- mConfigHasChanged |= configChanged;
- configChanged = mConfigHasChanged;
- }
-
- return configChanged;
+ return !mLastReportedConfiguration.equals(getConfiguration());
}
boolean isAdjustedForMinimizedDock() {
@@ -2132,10 +2280,10 @@
mTurnOnScreen = true;
}
if (isConfigChanged()) {
- final Configuration newConfig = updateConfiguration();
+ outConfig.setTo(getConfiguration());
if (DEBUG_CONFIGURATION) Slog.i(TAG, "Window " + this + " visible with new config: "
- + newConfig);
- outConfig.setTo(newConfig);
+ + outConfig);
+ mLastReportedConfiguration.setTo(outConfig);
}
}
@@ -2503,6 +2651,8 @@
return unfrozeWindows;
}
+ mAppFreezing = false;
+
if (mHasSurface && !mOrientationChanging
&& mService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "set mOrientationChanging of " + this);
@@ -2865,29 +3015,15 @@
}
}
- /**
- * Update our current configurations, based on task configuration.
- *
- * @return A configuration suitable for sending to the client.
- */
- private Configuration updateConfiguration() {
- final boolean configChanged = isConfigChanged();
- getMergedConfig(mMergedConfiguration);
- mConfigHasChanged = false;
- if ((DEBUG_RESIZE || DEBUG_ORIENTATION || DEBUG_CONFIGURATION) && configChanged) {
- Slog.i(TAG, "Sending new config to window " + this + ": " +
- " / mergedConfig=" + mMergedConfiguration);
- }
- return mMergedConfiguration;
- }
-
- private void getMergedConfig(Configuration outConfig) {
+ @Override
+ public Configuration getConfiguration() {
if (mAppToken != null && mAppToken.mFrozenMergedConfig.size() > 0) {
- outConfig.setTo(mAppToken.mFrozenMergedConfig.peek());
- return;
+ return mAppToken.mFrozenMergedConfig.peek();
}
- outConfig.setTo(
- mAppToken != null ? getConfiguration() : mDisplayContent.getConfiguration());
+
+ // TODO: Remove when all windows' hierarchies will start from same root.
+ return mAppToken != null
+ ? super.getConfiguration() : getDisplayContent().getConfiguration();
}
void reportResized() {
@@ -2895,7 +3031,13 @@
try {
if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Reporting new frame to " + this
+ ": " + mCompatFrame);
- final Configuration newConfig = isConfigChanged() ? updateConfiguration() : null;
+ final Configuration newConfig;
+ if (isConfigChanged()) {
+ newConfig = new Configuration(getConfiguration());
+ mLastReportedConfiguration.setTo(newConfig);
+ } else {
+ newConfig = null;
+ }
if (DEBUG_ORIENTATION && mWinAnimator.mDrawState == WindowStateAnimator.DRAW_PENDING)
Slog.i(TAG, "Resizing " + this + " WITH DRAW PENDING");
@@ -3217,7 +3359,9 @@
getTouchableRegion(region);
pw.print(prefix); pw.print("touchable region="); pw.println(region);
}
- pw.print(prefix); pw.print("mMergedConfiguration="); pw.println(mMergedConfiguration);
+ pw.print(prefix); pw.print("mFullConfiguration="); pw.println(getConfiguration());
+ pw.print(prefix); pw.print("mLastReportedConfiguration=");
+ pw.println(mLastReportedConfiguration);
}
pw.print(prefix); pw.print("mHasSurface="); pw.print(mHasSurface);
pw.print(" mShownPosition="); mShownPosition.printShortString(pw);
@@ -3922,7 +4066,7 @@
}
public boolean isRtl() {
- return mMergedConfiguration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+ return getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
}
void hideWallpaperWindow(boolean wasDeferred, String reason) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 6c7d136..dc83ac0 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -966,7 +966,7 @@
mDtDy = tmpFloats[Matrix.MSCALE_Y];
float x = tmpFloats[Matrix.MTRANS_X];
float y = tmpFloats[Matrix.MTRANS_Y];
- mWin.mShownPosition.set((int) x, (int) y);
+ mWin.mShownPosition.set(Math.round(x), Math.round(y));
// Now set the alpha... but because our current hardware
// can't do alpha transformation on a non-opaque surface,
@@ -1059,7 +1059,7 @@
mDtDy = tmpFloats[Matrix.MSCALE_Y];
float x = tmpFloats[Matrix.MTRANS_X];
float y = tmpFloats[Matrix.MTRANS_Y];
- mWin.mShownPosition.set((int) x, (int) y);
+ mWin.mShownPosition.set(Math.round(x), Math.round(y));
mShownAlpha = mAlpha;
} else {
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
new file mode 100644
index 0000000..5b565a7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
@@ -0,0 +1,341 @@
+
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.accounts;
+
+import android.accounts.Account;
+import android.content.Context;
+import android.database.Cursor;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Pair;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for {@link AccountsDb}.
+ * <p>Run with:<pre>
+ * m FrameworksServicesTests &&
+ * adb install \
+ * -r out/target/product/marlin/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
+ * adb shell am instrument -e class com.android.server.accounts.AccountsDbTest \
+ * -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+ * </pre>
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class AccountsDbTest {
+ private static final String PREN_DB = "pren.db";
+ private static final String DE_DB = "de.db";
+ private static final String CE_DB = "ce.db";
+
+ private AccountsDb mAccountsDb;
+ private File preNDb;
+ private File deDb;
+ private File ceDb;
+
+ @Before
+ public void setUp() {
+ Context context = InstrumentationRegistry.getContext();
+ preNDb = new File(context.getCacheDir(), PREN_DB);
+ ceDb = new File(context.getCacheDir(), CE_DB);
+ deDb = new File(context.getCacheDir(), DE_DB);
+ deleteDbFiles();
+ mAccountsDb = AccountsDb.create(context, 0, preNDb, deDb);
+ }
+
+ @After
+ public void tearDown() {
+ deleteDbFiles();
+ }
+
+ private void deleteDbFiles() {
+ AccountsDb.deleteDbFileWarnIfFailed(preNDb);
+ AccountsDb.deleteDbFileWarnIfFailed(ceDb);
+ AccountsDb.deleteDbFileWarnIfFailed(deDb);
+ }
+
+ @Test
+ public void testCeNotAvailableInitially() {
+ Account account = new Account("name", "example.com");
+ long id = mAccountsDb.insertCeAccount(account, "");
+ assertEquals("Insert into CE should fail until CE database is attached", -1, id);
+ }
+
+ @Test
+ public void testDeAccountInsertFindDelete() {
+ Account account = new Account("name", "example.com");
+ long accId = 1;
+ mAccountsDb.insertDeAccount(account, accId);
+ long actualId = mAccountsDb.findDeAccountId(account);
+ assertEquals(accId, actualId);
+ // Delete and verify that account no longer exists
+ mAccountsDb.deleteDeAccount(accId);
+ actualId = mAccountsDb.findDeAccountId(account);
+ assertEquals(-1, actualId);
+ }
+
+ @Test
+ public void testCeAccountInsertFindDelete() {
+ mAccountsDb.attachCeDatabase(ceDb);
+ Account account = new Account("name", "example.com");
+ long accId = mAccountsDb.insertCeAccount(account, "password");
+ long actualId = mAccountsDb.findCeAccountId(account);
+ assertEquals(accId, actualId);
+ // Delete and verify that account no longer exists
+ mAccountsDb.deleteCeAccount(accId);
+ actualId = mAccountsDb.findCeAccountId(account);
+ assertEquals(-1, actualId);
+ }
+
+ @Test
+ public void testAuthTokenInsertFindDelete() {
+ mAccountsDb.attachCeDatabase(ceDb);
+ Account account = new Account("name", "example.com");
+ long accId = mAccountsDb.insertCeAccount(account, "password");
+ mAccountsDb.insertDeAccount(account, accId);
+ long authTokenId = mAccountsDb.insertAuthToken(accId, "type", "token");
+ Map<String, String> authTokensByAccount = mAccountsDb.findAuthTokensByAccount(account);
+ assertEquals(1, authTokensByAccount.size());
+ try (Cursor cursor = mAccountsDb.findAuthtokenForAllAccounts(account.type, "token")) {
+ assertTrue(cursor.moveToNext());
+ }
+ try (Cursor cursor = mAccountsDb.findAuthtokenForAllAccounts(account.type, "nosuchtoken")) {
+ assertFalse(cursor.moveToNext());
+ }
+ mAccountsDb.deleteAuthToken(String.valueOf(authTokenId));
+ // Verify that token no longer exists
+ authTokensByAccount = mAccountsDb.findAuthTokensByAccount(account);
+ assertEquals(0, authTokensByAccount.size());
+ }
+
+ @Test
+ public void testAuthTokenDeletes() {
+ mAccountsDb.attachCeDatabase(ceDb);
+ // 1st account
+ Account account = new Account("name", "example.com");
+ long accId = mAccountsDb.insertCeAccount(account, "password");
+ mAccountsDb.insertDeAccount(account, accId);
+ mAccountsDb.insertAuthToken(accId, "type", "token");
+ mAccountsDb.insertAuthToken(accId, "type2", "token2");
+ // 2nd account
+ Account account2 = new Account("name", "example2.com");
+ long accId2 = mAccountsDb.insertCeAccount(account2, "password");
+ mAccountsDb.insertDeAccount(account2, accId);
+ mAccountsDb.insertAuthToken(accId2, "type", "token");
+
+ mAccountsDb.deleteAuthTokensByAccountId(accId2);
+ Map<String, String> authTokensByAccount = mAccountsDb.findAuthTokensByAccount(account2);
+ assertEquals(0, authTokensByAccount.size());
+ // Authtokens from account 1 are still there
+ authTokensByAccount = mAccountsDb.findAuthTokensByAccount(account);
+ assertEquals(2, authTokensByAccount.size());
+
+ // Delete authtokens from account 1 and verify
+ mAccountsDb.deleteAuthtokensByAccountIdAndType(accId, "type");
+ authTokensByAccount = mAccountsDb.findAuthTokensByAccount(account);
+ assertEquals(1, authTokensByAccount.size());
+ mAccountsDb.deleteAuthtokensByAccountIdAndType(accId, "type2");
+ authTokensByAccount = mAccountsDb.findAuthTokensByAccount(account);
+ assertEquals(0, authTokensByAccount.size());
+ }
+
+ @Test
+ public void testExtrasInsertFindDelete() {
+ mAccountsDb.attachCeDatabase(ceDb);
+ Account account = new Account("name", "example.com");
+ long accId = mAccountsDb.insertCeAccount(account, "password");
+ mAccountsDb.insertDeAccount(account, accId);
+ String extraKey = "extra_key";
+ String extraValue = "extra_value";
+ long extraId = mAccountsDb.insertExtra(accId, extraKey, extraValue);
+ // Test find methods
+ long actualExtraId = mAccountsDb.findExtrasIdByAccountId(accId, extraKey);
+ assertEquals(extraId, actualExtraId);
+ Map<String, String> extras = mAccountsDb.findUserExtrasForAccount(account);
+ assertEquals(1, extras.size());
+ assertEquals(extraValue, extras.get(extraKey));
+ // Test update
+ String newExtraValue = "extra_value2";
+ mAccountsDb.updateExtra(extraId, newExtraValue);
+ String newValue = mAccountsDb.findUserExtrasForAccount(account).get(extraKey);
+ assertEquals(newExtraValue, newValue);
+
+ // Delete account and verify that extras cascade removed
+ mAccountsDb.deleteCeAccount(accId);
+ actualExtraId = mAccountsDb.findExtrasIdByAccountId(accId, extraKey);
+ assertEquals(-1, actualExtraId);
+ }
+
+ @Test
+ public void testGrantsInsertFindDelete() {
+ mAccountsDb.attachCeDatabase(ceDb);
+ Account account = new Account("name", "example.com");
+ long accId = mAccountsDb.insertCeAccount(account, "password");
+ mAccountsDb.insertDeAccount(account, accId);
+ int testUid = 100500;
+ long grantId = mAccountsDb.insertGrant(accId, "tokenType", testUid);
+ assertTrue(grantId > 0);
+ List<Integer> allUidGrants = mAccountsDb.findAllUidGrants();
+ List<Integer> expectedUids = Arrays.asList(testUid);
+ assertEquals(expectedUids, allUidGrants);
+
+ long matchingGrantsCount = mAccountsDb.findMatchingGrantsCount(
+ testUid, "tokenType", account);
+ assertEquals(1, matchingGrantsCount);
+ // Test nonexistent type
+ matchingGrantsCount = mAccountsDb.findMatchingGrantsCount(
+ testUid, "noSuchType", account);
+ assertEquals(0, matchingGrantsCount);
+
+ matchingGrantsCount = mAccountsDb.findMatchingGrantsCountAnyToken(testUid, account);
+ assertEquals(1, matchingGrantsCount);
+
+ List<Pair<String, Integer>> allAccountGrants = mAccountsDb.findAllAccountGrants();
+ assertEquals(1, allAccountGrants.size());
+ assertEquals(account.name, allAccountGrants.get(0).first);
+ assertEquals(testUid, (int)allAccountGrants.get(0).second);
+
+ mAccountsDb.deleteGrantsByUid(testUid);
+ allUidGrants = mAccountsDb.findAllUidGrants();
+ assertTrue("Test grants should be removed", allUidGrants.isEmpty());
+ }
+
+ @Test
+ public void testSharedAccountsInsertFindDelete() {
+ Account account = new Account("name", "example.com");
+ long accId = 0;
+ mAccountsDb.insertDeAccount(account, accId);
+ long sharedAccId = mAccountsDb.insertSharedAccount(account);
+ long foundSharedAccountId = mAccountsDb.findSharedAccountId(account);
+ assertEquals(sharedAccId, foundSharedAccountId);
+ List<Account> sharedAccounts = mAccountsDb.getSharedAccounts();
+ List<Account> expectedList = Arrays.asList(account);
+ assertEquals(expectedList, sharedAccounts);
+
+ // Delete and verify
+ mAccountsDb.deleteSharedAccount(account);
+ foundSharedAccountId = mAccountsDb.findSharedAccountId(account);
+ assertEquals(-1, foundSharedAccountId);
+ }
+
+ @Test
+ public void testMetaInsertFindDelete() {
+ int testUid = 100500;
+ String authenticatorType = "authType";
+ mAccountsDb.insertOrReplaceMetaAuthTypeAndUid(authenticatorType, testUid);
+ Map<String, Integer> metaAuthUid = mAccountsDb.findMetaAuthUid();
+ assertEquals(1, metaAuthUid.size());
+ assertEquals(testUid, (int)metaAuthUid.get(authenticatorType));
+
+ // Delete and verify
+ boolean deleteResult = mAccountsDb.deleteMetaByAuthTypeAndUid(authenticatorType, testUid);
+ assertTrue(deleteResult);
+ metaAuthUid = mAccountsDb.findMetaAuthUid();
+ assertEquals(0, metaAuthUid.size());
+ }
+
+ @Test
+ public void testUpdateDeAccountLastAuthenticatedTime() {
+ Account account = new Account("name", "example.com");
+ long accId = 1;
+ mAccountsDb.insertDeAccount(account, accId);
+ long now = System.currentTimeMillis();
+ mAccountsDb.updateAccountLastAuthenticatedTime(account);
+ long time = mAccountsDb.findAccountLastAuthenticatedTime(account);
+ assertTrue("LastAuthenticatedTime should be current", time >= now);
+ }
+
+ @Test
+ public void testRenameAccount() {
+ mAccountsDb.attachCeDatabase(ceDb);
+ Account account = new Account("name", "example.com");
+ long accId = mAccountsDb.insertCeAccount(account, "password");
+ mAccountsDb.insertDeAccount(account, accId);
+ mAccountsDb.renameDeAccount(accId, "newName", "name");
+ Account newAccount = mAccountsDb.findAllDeAccounts().get(accId);
+ assertEquals("newName", newAccount.name);
+
+ String prevName = mAccountsDb.findDeAccountPreviousName(newAccount);
+ assertEquals("name", prevName);
+ mAccountsDb.renameCeAccount(accId, "newName");
+ long foundAccId = mAccountsDb.findCeAccountId(account);
+ assertEquals("Account shouldn't be found under the old name", -1, foundAccId);
+ foundAccId = mAccountsDb.findCeAccountId(newAccount);
+ assertEquals(accId, foundAccId);
+ }
+
+ @Test
+ public void testUpdateCeAccountPassword() {
+ mAccountsDb.attachCeDatabase(ceDb);
+ Account account = new Account("name", "example.com");
+ long accId = mAccountsDb.insertCeAccount(account, "password");
+ String newPassword = "newPassword";
+ mAccountsDb.updateCeAccountPassword(accId, newPassword);
+ String actualPassword = mAccountsDb
+ .findAccountPasswordByNameAndType(account.name, account.type);
+ assertEquals(newPassword, actualPassword);
+ }
+
+ @Test
+ public void testFindCeAccountsNotInDe() {
+ mAccountsDb.attachCeDatabase(ceDb);
+ Account account = new Account("name", "example.com");
+ long accId = mAccountsDb.insertCeAccount(account, "password");
+ mAccountsDb.insertDeAccount(account, accId);
+
+ Account accountNotInDe = new Account("name2", "example.com");
+ mAccountsDb.insertCeAccount(accountNotInDe, "password");
+
+ List<Account> ceAccounts = mAccountsDb.findCeAccountsNotInDe();
+ List<Account> expectedList = Arrays.asList(accountNotInDe);
+ assertEquals(expectedList, ceAccounts);
+ }
+
+ @Test
+ public void testCrossDbTransactions() {
+ mAccountsDb.attachCeDatabase(ceDb);
+ mAccountsDb.beginTransaction();
+ Account account = new Account("name", "example.com");
+ long accId;
+ accId = mAccountsDb.insertCeAccount(account, "password");
+ accId = mAccountsDb.insertDeAccount(account, accId);
+ long actualId = mAccountsDb.findCeAccountId(account);
+ assertEquals(accId, actualId);
+ actualId = mAccountsDb.findDeAccountId(account);
+ assertEquals(accId, actualId);
+ mAccountsDb.endTransaction();
+ // Verify that records were removed
+ actualId = mAccountsDb.findCeAccountId(account);
+ assertEquals(-1, actualId);
+ actualId = mAccountsDb.findDeAccountId(account);
+ assertEquals(-1, actualId);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/retaildemo/RetailDemoModeServiceTest.java b/services/tests/servicestests/src/com/android/server/retaildemo/RetailDemoModeServiceTest.java
index da4d150..e4473ad 100644
--- a/services/tests/servicestests/src/com/android/server/retaildemo/RetailDemoModeServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/retaildemo/RetailDemoModeServiceTest.java
@@ -147,6 +147,7 @@
userInfo.id = TEST_DEMO_USER;
when(mUm.createUser(anyString(), anyInt())).thenReturn(userInfo);
+ setCameraPackage(TEST_CAMERA_PKG);
mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
assertEquals(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED + " property not set",
"1", mInjector.systemPropertiesGet(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED));
@@ -157,7 +158,6 @@
assertTrue("Not registered for " + Intent.ACTION_SCREEN_OFF,
intentFilter.getValue().hasAction(Intent.ACTION_SCREEN_OFF));
- setCameraPackage(TEST_CAMERA_PKG);
// Wait for the setup to complete.
mLatch.await(SETUP_COMPLETE_TIMEOUT_MS, TimeUnit.MILLISECONDS);
ArgumentCaptor<Integer> flags = ArgumentCaptor.forClass(Integer.class);
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index fb1370e..9b62e14 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -1281,7 +1281,7 @@
const size_t NL = locales.size();
for (size_t i=0; i<NL; i++) {
const char* localeStr = locales[i].string();
- assets.setLocale(localeStr != NULL ? localeStr : "");
+ assets.setConfiguration(config, localeStr != NULL ? localeStr : "");
String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
&error);
if (llabel != "") {
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index 60114fb..b57d4db 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -24,7 +24,10 @@
sources := \
compile/IdAssigner.cpp \
compile/InlineXmlFormatParser.cpp \
+ compile/NinePatch.cpp \
compile/Png.cpp \
+ compile/PngChunkFilter.cpp \
+ compile/PngCrunch.cpp \
compile/PseudolocaleGenerator.cpp \
compile/Pseudolocalizer.cpp \
compile/XmlIdCollector.cpp \
@@ -34,12 +37,14 @@
flatten/XmlFlattener.cpp \
io/File.cpp \
io/FileSystem.cpp \
+ io/Io.cpp \
io/ZipArchive.cpp \
link/AutoVersioner.cpp \
link/ManifestFixer.cpp \
link/ProductFilter.cpp \
link/PrivateAttributeMover.cpp \
link/ReferenceLinker.cpp \
+ link/ResourceDeduper.cpp \
link/TableMerger.cpp \
link/VersionCollapser.cpp \
link/XmlNamespaceRemover.cpp \
@@ -56,6 +61,7 @@
util/Util.cpp \
ConfigDescription.cpp \
Debug.cpp \
+ DominatorTree.cpp \
Flags.cpp \
java/AnnotationProcessor.cpp \
java/ClassDefinition.cpp \
@@ -82,6 +88,7 @@
testSources := \
compile/IdAssigner_test.cpp \
compile/InlineXmlFormatParser_test.cpp \
+ compile/NinePatch_test.cpp \
compile/PseudolocaleGenerator_test.cpp \
compile/Pseudolocalizer_test.cpp \
compile/XmlIdCollector_test.cpp \
@@ -93,6 +100,7 @@
link/PrivateAttributeMover_test.cpp \
link/ProductFilter_test.cpp \
link/ReferenceLinker_test.cpp \
+ link/ResourceDeduper_test.cpp \
link/TableMerger_test.cpp \
link/VersionCollapser_test.cpp \
link/XmlNamespaceRemover_test.cpp \
@@ -106,6 +114,7 @@
util/StringPiece_test.cpp \
util/Util_test.cpp \
ConfigDescription_test.cpp \
+ DominatorTree_test.cpp \
java/AnnotationProcessor_test.cpp \
java/JavaClassGenerator_test.cpp \
java/ManifestClassGenerator_test.cpp \
diff --git a/tools/aapt2/ConfigDescription.cpp b/tools/aapt2/ConfigDescription.cpp
index c1697e7..1812d96 100644
--- a/tools/aapt2/ConfigDescription.cpp
+++ b/tools/aapt2/ConfigDescription.cpp
@@ -789,4 +789,96 @@
return copy;
}
+bool ConfigDescription::dominates(const ConfigDescription& o) const {
+ if (*this == defaultConfig() || *this == o) {
+ return true;
+ }
+ return matchWithDensity(o)
+ && !o.matchWithDensity(*this)
+ && !isMoreSpecificThan(o)
+ && !o.hasHigherPrecedenceThan(*this);
+}
+
+bool ConfigDescription::hasHigherPrecedenceThan(const ConfigDescription& o) const {
+ // The order of the following tests defines the importance of one
+ // configuration parameter over another. Those tests first are more
+ // important, trumping any values in those following them.
+ // The ordering should be the same as ResTable_config#isBetterThan.
+ if (mcc || o.mcc) return (!o.mcc);
+ if (mnc || o.mnc) return (!o.mnc);
+ if (language[0] || o.language[0]) return (!o.language[0]);
+ if (country[0] || o.country[0]) return (!o.country[0]);
+ // Script and variant require either a language or country, both of which
+ // have higher precedence.
+ if ((screenLayout | o.screenLayout) & MASK_LAYOUTDIR) {
+ return !(o.screenLayout & MASK_LAYOUTDIR);
+ }
+ if (smallestScreenWidthDp || o.smallestScreenWidthDp) return (!o.smallestScreenWidthDp);
+ if (screenWidthDp || o.screenWidthDp) return (!o.screenWidthDp);
+ if (screenHeightDp || o.screenHeightDp) return (!o.screenHeightDp);
+ if ((screenLayout | o.screenLayout) & MASK_SCREENSIZE) {
+ return !(o.screenLayout & MASK_SCREENSIZE);
+ }
+ if ((screenLayout | o.screenLayout) & MASK_SCREENLONG) {
+ return !(o.screenLayout & MASK_SCREENLONG);
+ }
+ if ((screenLayout2 | o.screenLayout2) & MASK_SCREENROUND) {
+ return !(o.screenLayout2 & MASK_SCREENROUND);
+ }
+ if (orientation || o.orientation) return (!o.orientation);
+ if ((uiMode | o.uiMode) & MASK_UI_MODE_TYPE) {
+ return !(o.uiMode & MASK_UI_MODE_TYPE);
+ }
+ if ((uiMode | o.uiMode) & MASK_UI_MODE_NIGHT) {
+ return !(o.uiMode & MASK_UI_MODE_NIGHT);
+ }
+ if (density || o.density) return (!o.density);
+ if (touchscreen || o.touchscreen) return (!o.touchscreen);
+ if ((inputFlags | o.inputFlags) & MASK_KEYSHIDDEN) {
+ return !(o.inputFlags & MASK_KEYSHIDDEN);
+ }
+ if ((inputFlags | o.inputFlags) & MASK_NAVHIDDEN) {
+ return !(o.inputFlags & MASK_NAVHIDDEN);
+ }
+ if (keyboard || o.keyboard) return (!o.keyboard);
+ if (navigation || o.navigation) return (!o.navigation);
+ if (screenWidth || o.screenWidth) return (!o.screenWidth);
+ if (screenHeight || o.screenHeight) return (!o.screenHeight);
+ if (sdkVersion || o.sdkVersion) return (!o.sdkVersion);
+ if (minorVersion || o.minorVersion) return (!o.minorVersion);
+ // Both configurations have nothing defined except some possible future
+ // value. Returning the comparison of the two configurations is a
+ // "best effort" at this point to protect against incorrect dominations.
+ return *this != o;
+}
+
+bool ConfigDescription::conflictsWith(const ConfigDescription& o) const {
+ // This method should be updated as new configuration parameters are
+ // introduced (e.g. screenConfig2).
+ auto pred = [](const uint32_t a, const uint32_t b) -> bool {
+ return a == 0 || b == 0 || a == b;
+ };
+ // The values here can be found in ResTable_config#match. Density and range
+ // values can't lead to conflicts, and are ignored.
+ return !pred(mcc, o.mcc)
+ || !pred(mnc, o.mnc)
+ || !pred(locale, o.locale)
+ || !pred(screenLayout & MASK_LAYOUTDIR, o.screenLayout & MASK_LAYOUTDIR)
+ || !pred(screenLayout & MASK_SCREENLONG, o.screenLayout & MASK_SCREENLONG)
+ || !pred(screenLayout & MASK_UI_MODE_TYPE, o.screenLayout & MASK_UI_MODE_TYPE)
+ || !pred(uiMode & MASK_UI_MODE_TYPE, o.uiMode & MASK_UI_MODE_TYPE)
+ || !pred(uiMode & MASK_UI_MODE_NIGHT, o.uiMode & MASK_UI_MODE_NIGHT)
+ || !pred(screenLayout2 & MASK_SCREENROUND, o.screenLayout2 & MASK_SCREENROUND)
+ || !pred(orientation, o.orientation)
+ || !pred(touchscreen, o.touchscreen)
+ || !pred(inputFlags & MASK_KEYSHIDDEN, o.inputFlags & MASK_KEYSHIDDEN)
+ || !pred(inputFlags & MASK_NAVHIDDEN, o.inputFlags & MASK_NAVHIDDEN)
+ || !pred(keyboard, o.keyboard)
+ || !pred(navigation, o.navigation);
+}
+
+bool ConfigDescription::isCompatibleWith(const ConfigDescription& o) const {
+ return !conflictsWith(o) && !dominates(o) && !o.dominates(*this);
+}
+
} // namespace aapt
diff --git a/tools/aapt2/ConfigDescription.h b/tools/aapt2/ConfigDescription.h
index 6858c62..d801621 100644
--- a/tools/aapt2/ConfigDescription.h
+++ b/tools/aapt2/ConfigDescription.h
@@ -59,14 +59,50 @@
ConfigDescription& operator=(const ConfigDescription& o);
ConfigDescription& operator=(ConfigDescription&& o);
+ ConfigDescription copyWithoutSdkVersion() const;
+
+ /**
+ * A configuration X dominates another configuration Y, if X has at least the
+ * precedence of Y and X is strictly more general than Y: for any type defined
+ * by X, the same type is defined by Y with a value equal to or, in the case
+ * of ranges, more specific than that of X.
+ *
+ * For example, the configuration 'en-w800dp' dominates 'en-rGB-w1024dp'. It
+ * does not dominate 'fr', 'en-w720dp', or 'mcc001-en-w800dp'.
+ */
+ bool dominates(const ConfigDescription& o) const;
+
+ /**
+ * Returns true if this configuration defines a more important configuration
+ * parameter than o. For example, "en" has higher precedence than "v23",
+ * whereas "en" has the same precedence as "en-v23".
+ */
+ bool hasHigherPrecedenceThan(const ConfigDescription& o) const;
+
+ /**
+ * A configuration conflicts with another configuration if both
+ * configurations define an incompatible configuration parameter. An
+ * incompatible configuration parameter is a non-range, non-density parameter
+ * that is defined in both configurations as a different, non-default value.
+ */
+ bool conflictsWith(const ConfigDescription& o) const;
+
+ /**
+ * A configuration is compatible with another configuration if both
+ * configurations can match a common concrete device configuration and are
+ * unrelated by domination. For example, land-v11 conflicts with port-v21
+ * but is compatible with v21 (both land-v11 and v21 would match en-land-v23).
+ */
+ bool isCompatibleWith(const ConfigDescription& o) const;
+
+ bool matchWithDensity(const ConfigDescription& o) const;
+
bool operator<(const ConfigDescription& o) const;
bool operator<=(const ConfigDescription& o) const;
bool operator==(const ConfigDescription& o) const;
bool operator!=(const ConfigDescription& o) const;
bool operator>=(const ConfigDescription& o) const;
bool operator>(const ConfigDescription& o) const;
-
- ConfigDescription copyWithoutSdkVersion() const;
};
inline ConfigDescription::ConfigDescription() {
@@ -103,6 +139,10 @@
return *this;
}
+inline bool ConfigDescription::matchWithDensity(const ConfigDescription& o) const {
+ return match(o) && (density == 0 || density == o.density);
+}
+
inline bool ConfigDescription::operator<(const ConfigDescription& o) const {
return compare(o) < 0;
}
diff --git a/tools/aapt2/DominatorTree.cpp b/tools/aapt2/DominatorTree.cpp
new file mode 100644
index 0000000..29587a8
--- /dev/null
+++ b/tools/aapt2/DominatorTree.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "ConfigDescription.h"
+#include "DominatorTree.h"
+
+#include <algorithm>
+
+namespace aapt {
+
+DominatorTree::DominatorTree(
+ const std::vector<std::unique_ptr<ResourceConfigValue>>& configs) {
+ for (const auto& config : configs) {
+ mProductRoots[config->product].tryAddChild(
+ util::make_unique<Node>(config.get(), nullptr));
+ }
+}
+
+void DominatorTree::accept(Visitor* visitor) {
+ for (auto& entry : mProductRoots) {
+ visitor->visitTree(entry.first, &entry.second);
+ }
+}
+
+bool DominatorTree::Node::tryAddChild(std::unique_ptr<Node> newChild) {
+ assert(newChild->mValue && "cannot add a root or empty node as a child");
+ if (mValue && !dominates(newChild.get())) {
+ // This is not the root and the child dominates us.
+ return false;
+ }
+ return addChild(std::move(newChild));
+}
+
+bool DominatorTree::Node::addChild(std::unique_ptr<Node> newChild) {
+ bool hasDominatedChildren = false;
+ // Demote children dominated by the new config.
+ for (auto& child : mChildren) {
+ if (newChild->dominates(child.get())) {
+ child->mParent = newChild.get();
+ newChild->mChildren.push_back(std::move(child));
+ child = {};
+ hasDominatedChildren = true;
+ }
+ }
+ // Remove dominated children.
+ if (hasDominatedChildren) {
+ mChildren.erase(std::remove_if(mChildren.begin(), mChildren.end(),
+ [](const std::unique_ptr<Node>& child) -> bool {
+ return child == nullptr;
+ }), mChildren.end());
+ }
+ // Add the new config to a child if a child dominates the new config.
+ for (auto& child : mChildren) {
+ if (child->dominates(newChild.get())) {
+ child->addChild(std::move(newChild));
+ return true;
+ }
+ }
+ // The new config is not dominated by a child, so add it here.
+ newChild->mParent = this;
+ mChildren.push_back(std::move(newChild));
+ return true;
+}
+
+bool DominatorTree::Node::dominates(const Node* other) const {
+ // Check root node dominations.
+ if (other->isRootNode()) {
+ return isRootNode();
+ } else if (isRootNode()) {
+ return true;
+ }
+ // Neither node is a root node; compare the configurations.
+ return mValue->config.dominates(other->mValue->config);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/DominatorTree.h b/tools/aapt2/DominatorTree.h
new file mode 100644
index 0000000..ad2df0e
--- /dev/null
+++ b/tools/aapt2/DominatorTree.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef AAPT_DOMINATOR_TREE_H
+#define AAPT_DOMINATOR_TREE_H
+
+#include "ResourceTable.h"
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace aapt {
+
+/**
+ * A dominator tree of configurations as defined by resolution rules for Android
+ * resources.
+ *
+ * A node in the tree represents a resource configuration.
+ *
+ * The tree has the following property:
+ *
+ * Each child of a given configuration defines a strict superset of qualifiers
+ * and has a value that is at least as specific as that of its ancestors. A
+ * value is "at least as specific" if it is either identical or it represents a
+ * stronger requirement.
+ * For example, v21 is more specific than v11, and w1200dp is more specific than
+ * w800dp.
+ *
+ * The dominator tree relies on the underlying configurations passed to it. If
+ * the configurations passed to the dominator tree go out of scope, the tree
+ * will exhibit undefined behavior.
+ */
+class DominatorTree {
+public:
+ explicit DominatorTree(const std::vector<std::unique_ptr<ResourceConfigValue>>& configs);
+
+ class Node {
+ public:
+ explicit Node(ResourceConfigValue* value = nullptr, Node* parent = nullptr) :
+ mValue(value), mParent(parent) {
+ }
+
+ inline ResourceConfigValue* value() const {
+ return mValue;
+ }
+
+ inline Node* parent() const {
+ return mParent;
+ }
+
+ inline bool isRootNode() const {
+ return !mValue;
+ }
+
+ inline const std::vector<std::unique_ptr<Node>>& children() const {
+ return mChildren;
+ }
+
+ bool tryAddChild(std::unique_ptr<Node> newChild);
+
+ private:
+ bool addChild(std::unique_ptr<Node> newChild);
+ bool dominates(const Node* other) const;
+
+ ResourceConfigValue* mValue;
+ Node* mParent;
+ std::vector<std::unique_ptr<Node>> mChildren;
+
+ DISALLOW_COPY_AND_ASSIGN(Node);
+ };
+
+ struct Visitor {
+ virtual ~Visitor() = default;
+ virtual void visitTree(const std::string& product, Node* root) = 0;
+ };
+
+ class BottomUpVisitor : public Visitor {
+ public:
+ virtual ~BottomUpVisitor() = default;
+
+ void visitTree(const std::string& product, Node* root) override {
+ for (auto& child : root->children()) {
+ visitNode(child.get());
+ }
+ }
+
+ virtual void visitConfig(Node* node) = 0;
+
+ private:
+ void visitNode(Node* node) {
+ for (auto& child : node->children()) {
+ visitNode(child.get());
+ }
+ visitConfig(node);
+ }
+ };
+
+ void accept(Visitor* visitor);
+
+ inline const std::map<std::string, Node>& getProductRoots() const {
+ return mProductRoots;
+ }
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(DominatorTree);
+
+ std::map<std::string, Node> mProductRoots;
+};
+
+} // namespace aapt
+
+#endif // AAPT_DOMINATOR_TREE_H
diff --git a/tools/aapt2/DominatorTree_test.cpp b/tools/aapt2/DominatorTree_test.cpp
new file mode 100644
index 0000000..fb850e4
--- /dev/null
+++ b/tools/aapt2/DominatorTree_test.cpp
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "DominatorTree.h"
+#include "test/Test.h"
+#include "util/Util.h"
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace aapt {
+
+namespace {
+
+class PrettyPrinter : public DominatorTree::Visitor {
+public:
+ explicit PrettyPrinter(const int indent = 2) : mIndent(indent) {
+ }
+
+ void visitTree(const std::string& product, DominatorTree::Node* root) override {
+ for (auto& child : root->children()) {
+ visitNode(child.get(), 0);
+ }
+ }
+
+ std::string toString(DominatorTree* tree) {
+ mBuffer.str("");
+ mBuffer.clear();
+ tree->accept(this);
+ return mBuffer.str();
+ }
+
+private:
+ void visitConfig(const DominatorTree::Node* node, const int indent) {
+ auto configString = node->value()->config.toString();
+ mBuffer << std::string(indent, ' ')
+ << (configString.isEmpty() ? "<default>" : configString)
+ << std::endl;
+ }
+
+ void visitNode(const DominatorTree::Node* node, const int indent) {
+ visitConfig(node, indent);
+ for (const auto& child : node->children()) {
+ visitNode(child.get(), indent + mIndent);
+ }
+ }
+
+ std::stringstream mBuffer;
+ const int mIndent = 2;
+};
+
+} // namespace
+
+TEST(DominatorTreeTest, DefaultDominatesEverything) {
+ const ConfigDescription defaultConfig = {};
+ const ConfigDescription landConfig = test::parseConfigOrDie("land");
+ const ConfigDescription sw600dpLandConfig = test::parseConfigOrDie("sw600dp-land-v13");
+
+ std::vector<std::unique_ptr<ResourceConfigValue>> configs;
+ configs.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(landConfig, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(sw600dpLandConfig, ""));
+
+ DominatorTree tree(configs);
+ PrettyPrinter printer;
+
+ std::string expected =
+ "<default>\n"
+ " land\n"
+ " sw600dp-land-v13\n";
+ EXPECT_EQ(expected, printer.toString(&tree));
+}
+
+TEST(DominatorTreeTest, ProductsAreDominatedSeparately) {
+ const ConfigDescription defaultConfig = {};
+ const ConfigDescription landConfig = test::parseConfigOrDie("land");
+ const ConfigDescription sw600dpLandConfig = test::parseConfigOrDie("sw600dp-land-v13");
+
+ std::vector<std::unique_ptr<ResourceConfigValue>> configs;
+ configs.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(landConfig, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, "phablet"));
+ configs.push_back(util::make_unique<ResourceConfigValue>(sw600dpLandConfig, "phablet"));
+
+ DominatorTree tree(configs);
+ PrettyPrinter printer;
+
+ std::string expected =
+ "<default>\n"
+ " land\n"
+ "<default>\n"
+ " sw600dp-land-v13\n";
+ EXPECT_EQ(expected, printer.toString(&tree));
+}
+
+TEST(DominatorTreeTest, MoreSpecificConfigurationsAreDominated) {
+ const ConfigDescription defaultConfig = {};
+ const ConfigDescription enConfig = test::parseConfigOrDie("en");
+ const ConfigDescription enV21Config = test::parseConfigOrDie("en-v21");
+ const ConfigDescription ldrtlConfig = test::parseConfigOrDie("ldrtl-v4");
+ const ConfigDescription ldrtlXhdpiConfig = test::parseConfigOrDie("ldrtl-xhdpi-v4");
+ const ConfigDescription sw300dpConfig = test::parseConfigOrDie("sw300dp-v13");
+ const ConfigDescription sw540dpConfig = test::parseConfigOrDie("sw540dp-v14");
+ const ConfigDescription sw600dpConfig = test::parseConfigOrDie("sw600dp-v14");
+ const ConfigDescription sw720dpConfig = test::parseConfigOrDie("sw720dp-v13");
+ const ConfigDescription v20Config = test::parseConfigOrDie("v20");
+
+ std::vector<std::unique_ptr<ResourceConfigValue>> configs;
+ configs.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(enConfig, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(enV21Config, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(ldrtlConfig, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(ldrtlXhdpiConfig, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(sw300dpConfig, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(sw540dpConfig, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(sw600dpConfig, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(sw720dpConfig, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(v20Config, ""));
+
+ DominatorTree tree(configs);
+ PrettyPrinter printer;
+
+ std::string expected =
+ "<default>\n"
+ " en\n"
+ " en-v21\n"
+ " ldrtl-v4\n"
+ " ldrtl-xhdpi-v4\n"
+ " sw300dp-v13\n"
+ " sw540dp-v14\n"
+ " sw600dp-v14\n"
+ " sw720dp-v13\n"
+ " v20\n";
+ EXPECT_EQ(expected, printer.toString(&tree));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/compile/Compile.cpp b/tools/aapt2/compile/Compile.cpp
index e0f37ec..dbd8062 100644
--- a/tools/aapt2/compile/Compile.cpp
+++ b/tools/aapt2/compile/Compile.cpp
@@ -36,6 +36,8 @@
#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
#include <google/protobuf/io/coded_stream.h>
+#include <android-base/errors.h>
+#include <android-base/file.h>
#include <dirent.h>
#include <fstream>
#include <string>
@@ -359,6 +361,9 @@
static bool compileXml(IAaptContext* context, const CompileOptions& options,
const ResourcePathData& pathData, IArchiveWriter* writer,
const std::string& outputPath) {
+ if (context->verbose()) {
+ context->getDiagnostics()->note(DiagMessage(pathData.source) << "compiling XML");
+ }
std::unique_ptr<xml::XmlResource> xmlRes;
{
@@ -431,9 +436,43 @@
return true;
}
+class BigBufferOutputStream : public io::OutputStream {
+public:
+ explicit BigBufferOutputStream(BigBuffer* buffer) : mBuffer(buffer) {
+ }
+
+ bool Next(void** data, int* len) override {
+ size_t count;
+ *data = mBuffer->nextBlock(&count);
+ *len = static_cast<int>(count);
+ return true;
+ }
+
+ void BackUp(int count) override {
+ mBuffer->backUp(count);
+ }
+
+ int64_t ByteCount() const override {
+ return mBuffer->size();
+ }
+
+ bool HadError() const override {
+ return false;
+ }
+
+private:
+ BigBuffer* mBuffer;
+
+ DISALLOW_COPY_AND_ASSIGN(BigBufferOutputStream);
+};
+
static bool compilePng(IAaptContext* context, const CompileOptions& options,
const ResourcePathData& pathData, IArchiveWriter* writer,
const std::string& outputPath) {
+ if (context->verbose()) {
+ context->getDiagnostics()->note(DiagMessage(pathData.source) << "compiling PNG");
+ }
+
BigBuffer buffer(4096);
ResourceFile resFile;
resFile.name = ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
@@ -441,16 +480,90 @@
resFile.source = pathData.source;
{
- std::ifstream fin(pathData.source.path, std::ifstream::binary);
- if (!fin) {
- context->getDiagnostics()->error(DiagMessage(pathData.source) << strerror(errno));
+ std::string content;
+ if (!android::base::ReadFileToString(pathData.source.path, &content)) {
+ context->getDiagnostics()->error(DiagMessage(pathData.source)
+ << android::base::SystemErrorCodeToString(errno));
return false;
}
- Png png(context->getDiagnostics());
- if (!png.process(pathData.source, &fin, &buffer, {})) {
+ BigBuffer crunchedPngBuffer(4096);
+ BigBufferOutputStream crunchedPngBufferOut(&crunchedPngBuffer);
+
+ // Ensure that we only keep the chunks we care about if we end up
+ // using the original PNG instead of the crunched one.
+ PngChunkFilter pngChunkFilter(content);
+ std::unique_ptr<Image> image = readPng(context, &pngChunkFilter);
+ if (!image) {
return false;
}
+
+ std::unique_ptr<NinePatch> ninePatch;
+ if (pathData.extension == "9.png") {
+ std::string err;
+ ninePatch = NinePatch::create(image->rows.get(), image->width, image->height, &err);
+ if (!ninePatch) {
+ context->getDiagnostics()->error(DiagMessage() << err);
+ return false;
+ }
+
+ // Remove the 1px border around the NinePatch.
+ // Basically the row array is shifted up by 1, and the length is treated
+ // as height - 2.
+ // For each row, shift the array to the left by 1, and treat the length as width - 2.
+ image->width -= 2;
+ image->height -= 2;
+ memmove(image->rows.get(), image->rows.get() + 1, image->height * sizeof(uint8_t**));
+ for (int32_t h = 0; h < image->height; h++) {
+ memmove(image->rows[h], image->rows[h] + 4, image->width * 4);
+ }
+
+ if (context->verbose()) {
+ context->getDiagnostics()->note(DiagMessage(pathData.source)
+ << "9-patch: " << *ninePatch);
+ }
+ }
+
+ // Write the crunched PNG.
+ if (!writePng(context, image.get(), ninePatch.get(), &crunchedPngBufferOut, {})) {
+ return false;
+ }
+
+ if (ninePatch != nullptr
+ || crunchedPngBufferOut.ByteCount() <= pngChunkFilter.ByteCount()) {
+ // No matter what, we must use the re-encoded PNG, even if it is larger.
+ // 9-patch images must be re-encoded since their borders are stripped.
+ buffer.appendBuffer(std::move(crunchedPngBuffer));
+ } else {
+ // The re-encoded PNG is larger than the original, and there is
+ // no mandatory transformation. Use the original.
+ if (context->verbose()) {
+ context->getDiagnostics()->note(DiagMessage(pathData.source)
+ << "original PNG is smaller than crunched PNG"
+ << ", using original");
+ }
+
+ PngChunkFilter pngChunkFilterAgain(content);
+ BigBuffer filteredPngBuffer(4096);
+ BigBufferOutputStream filteredPngBufferOut(&filteredPngBuffer);
+ io::copy(&filteredPngBufferOut, &pngChunkFilterAgain);
+ buffer.appendBuffer(std::move(filteredPngBuffer));
+ }
+
+ if (context->verbose()) {
+ // For debugging only, use the legacy PNG cruncher and compare the resulting file sizes.
+ // This will help catch exotic cases where the new code may generate larger PNGs.
+ std::stringstream legacyStream(content);
+ BigBuffer legacyBuffer(4096);
+ Png png(context->getDiagnostics());
+ if (!png.process(pathData.source, &legacyStream, &legacyBuffer, {})) {
+ return false;
+ }
+
+ context->getDiagnostics()->note(DiagMessage(pathData.source)
+ << "legacy=" << legacyBuffer.size()
+ << " new=" << buffer.size());
+ }
}
if (!writeHeaderAndBufferToWriter(outputPath, resFile, buffer, writer,
@@ -463,6 +576,10 @@
static bool compileFile(IAaptContext* context, const CompileOptions& options,
const ResourcePathData& pathData, IArchiveWriter* writer,
const std::string& outputPath) {
+ if (context->verbose()) {
+ context->getDiagnostics()->note(DiagMessage(pathData.source) << "compiling file");
+ }
+
BigBuffer buffer(256);
ResourceFile resFile;
resFile.name = ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
diff --git a/tools/aapt2/compile/Image.h b/tools/aapt2/compile/Image.h
new file mode 100644
index 0000000..fda6a3a
--- /dev/null
+++ b/tools/aapt2/compile/Image.h
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef AAPT_COMPILE_IMAGE_H
+#define AAPT_COMPILE_IMAGE_H
+
+#include <android-base/macros.h>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace aapt {
+
+/**
+ * An in-memory image, loaded from disk, with pixels in RGBA_8888 format.
+ */
+class Image {
+public:
+ explicit Image() = default;
+
+ /**
+ * A `height` sized array of pointers, where each element points to a
+ * `width` sized row of RGBA_8888 pixels.
+ */
+ std::unique_ptr<uint8_t*[]> rows;
+
+ /**
+ * The width of the image in RGBA_8888 pixels. This is int32_t because of 9-patch data
+ * format limitations.
+ */
+ int32_t width = 0;
+
+ /**
+ * The height of the image in RGBA_8888 pixels. This is int32_t because of 9-patch data
+ * format limitations.
+ */
+ int32_t height = 0;
+
+ /**
+ * Buffer to the raw image data stored sequentially.
+ * Use `rows` to access the data on a row-by-row basis.
+ */
+ std::unique_ptr<uint8_t[]> data;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(Image);
+};
+
+/**
+ * A range of pixel values, starting at 'start' and ending before 'end' exclusive. Or rather [a, b).
+ */
+struct Range {
+ int32_t start = 0;
+ int32_t end = 0;
+
+ explicit Range() = default;
+ inline explicit Range(int32_t s, int32_t e) : start(s), end(e) {
+ }
+};
+
+inline bool operator==(const Range& left, const Range& right) {
+ return left.start == right.start && left.end == right.end;
+}
+
+/**
+ * Inset lengths from all edges of a rectangle. `left` and `top` are measured from the left and top
+ * edges, while `right` and `bottom` are measured from the right and bottom edges, respectively.
+ */
+struct Bounds {
+ int32_t left = 0;
+ int32_t top = 0;
+ int32_t right = 0;
+ int32_t bottom = 0;
+
+ explicit Bounds() = default;
+ inline explicit Bounds(int32_t l, int32_t t, int32_t r, int32_t b) :
+ left(l), top(t), right(r), bottom(b) {
+ }
+
+ bool nonZero() const;
+};
+
+inline bool Bounds::nonZero() const {
+ return left != 0 || top != 0 || right != 0 || bottom != 0;
+}
+
+inline bool operator==(const Bounds& left, const Bounds& right) {
+ return left.left == right.left && left.top == right.top &&
+ left.right == right.right && left.bottom == right.bottom;
+}
+
+/**
+ * Contains 9-patch data from a source image. All measurements exclude the 1px border of the
+ * source 9-patch image.
+ */
+class NinePatch {
+public:
+ static std::unique_ptr<NinePatch> create(uint8_t** rows,
+ const int32_t width, const int32_t height,
+ std::string* errOut);
+
+ /**
+ * Packs the RGBA_8888 data pointed to by pixel into a uint32_t
+ * with format 0xAARRGGBB (the way 9-patch expects it).
+ */
+ static uint32_t packRGBA(const uint8_t* pixel);
+
+ /**
+ * 9-patch content padding/insets. All positions are relative to the 9-patch
+ * NOT including the 1px thick source border.
+ */
+ Bounds padding;
+
+ /**
+ * Optical layout bounds/insets. This overrides the padding for
+ * layout purposes. All positions are relative to the 9-patch
+ * NOT including the 1px thick source border.
+ * See https://developer.android.com/about/versions/android-4.3.html#OpticalBounds
+ */
+ Bounds layoutBounds;
+
+ /**
+ * Outline of the image, calculated based on opacity.
+ */
+ Bounds outline;
+
+ /**
+ * The computed radius of the outline. If non-zero, the outline is a rounded-rect.
+ */
+ float outlineRadius = 0.0f;
+
+ /**
+ * The largest alpha value within the outline.
+ */
+ uint32_t outlineAlpha = 0x000000ffu;
+
+ /**
+ * Horizontal regions of the image that are stretchable.
+ * All positions are relative to the 9-patch
+ * NOT including the 1px thick source border.
+ */
+ std::vector<Range> horizontalStretchRegions;
+
+ /**
+ * Vertical regions of the image that are stretchable.
+ * All positions are relative to the 9-patch
+ * NOT including the 1px thick source border.
+ */
+ std::vector<Range> verticalStretchRegions;
+
+ /**
+ * The colors within each region, fixed or stretchable.
+ * For w*h regions, the color of region (x,y) is addressable
+ * via index y*w + x.
+ */
+ std::vector<uint32_t> regionColors;
+
+ /**
+ * Returns serialized data containing the original basic 9-patch meta data.
+ * Optical layout bounds and round rect outline data must be serialized
+ * separately using serializeOpticalLayoutBounds() and serializeRoundedRectOutline().
+ */
+ std::unique_ptr<uint8_t[]> serializeBase(size_t* outLen) const;
+
+ /**
+ * Serializes the layout bounds.
+ */
+ std::unique_ptr<uint8_t[]> serializeLayoutBounds(size_t* outLen) const;
+
+ /**
+ * Serializes the rounded-rect outline.
+ */
+ std::unique_ptr<uint8_t[]> serializeRoundedRectOutline(size_t* outLen) const;
+
+private:
+ explicit NinePatch() = default;
+
+ DISALLOW_COPY_AND_ASSIGN(NinePatch);
+};
+
+::std::ostream& operator<<(::std::ostream& out, const Range& range);
+::std::ostream& operator<<(::std::ostream& out, const Bounds& bounds);
+::std::ostream& operator<<(::std::ostream& out, const NinePatch& ninePatch);
+
+} // namespace aapt
+
+#endif /* AAPT_COMPILE_IMAGE_H */
diff --git a/tools/aapt2/compile/NinePatch.cpp b/tools/aapt2/compile/NinePatch.cpp
new file mode 100644
index 0000000..0fc1c5d
--- /dev/null
+++ b/tools/aapt2/compile/NinePatch.cpp
@@ -0,0 +1,676 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "compile/Image.h"
+#include "util/StringPiece.h"
+#include "util/Util.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace aapt {
+
+// Colors in the format 0xAARRGGBB (the way 9-patch expects it).
+constexpr static const uint32_t kColorOpaqueWhite = 0xffffffffu;
+constexpr static const uint32_t kColorOpaqueBlack = 0xff000000u;
+constexpr static const uint32_t kColorOpaqueRed = 0xffff0000u;
+
+constexpr static const uint32_t kPrimaryColor = kColorOpaqueBlack;
+constexpr static const uint32_t kSecondaryColor = kColorOpaqueRed;
+
+/**
+ * Returns the alpha value encoded in the 0xAARRGBB encoded pixel.
+ */
+static uint32_t getAlpha(uint32_t color);
+
+/**
+ * Determines whether a color on an ImageLine is valid.
+ * A 9patch image may use a transparent color as neutral,
+ * or a fully opaque white color as neutral, based on the
+ * pixel color at (0,0) of the image. One or the other is fine,
+ * but we need to ensure consistency throughout the image.
+ */
+class ColorValidator {
+public:
+ virtual ~ColorValidator() = default;
+
+ /**
+ * Returns true if the color specified is a neutral color
+ * (no padding, stretching, or optical bounds).
+ */
+ virtual bool isNeutralColor(uint32_t color) const = 0;
+
+ /**
+ * Returns true if the color is either a neutral color
+ * or one denoting padding, stretching, or optical bounds.
+ */
+ bool isValidColor(uint32_t color) const {
+ switch (color) {
+ case kPrimaryColor:
+ case kSecondaryColor:
+ return true;
+ }
+ return isNeutralColor(color);
+ }
+};
+
+// Walks an ImageLine and records Ranges of primary and secondary colors.
+// The primary color is black and is used to denote a padding or stretching range,
+// depending on which border we're iterating over.
+// The secondary color is red and is used to denote optical bounds.
+//
+// An ImageLine is a templated-interface that would look something like this if it
+// were polymorphic:
+//
+// class ImageLine {
+// public:
+// virtual int32_t getLength() const = 0;
+// virtual uint32_t getColor(int32_t idx) const = 0;
+// };
+//
+template <typename ImageLine>
+static bool fillRanges(const ImageLine* imageLine,
+ const ColorValidator* colorValidator,
+ std::vector<Range>* primaryRanges,
+ std::vector<Range>* secondaryRanges,
+ std::string* err) {
+ const int32_t length = imageLine->getLength();
+
+ uint32_t lastColor = 0xffffffffu;
+ for (int32_t idx = 1; idx < length - 1; idx++) {
+ const uint32_t color = imageLine->getColor(idx);
+ if (!colorValidator->isValidColor(color)) {
+ *err = "found an invalid color";
+ return false;
+ }
+
+ if (color != lastColor) {
+ // We are ending a range. Which range?
+ // note: encode the x offset without the final 1 pixel border.
+ if (lastColor == kPrimaryColor) {
+ primaryRanges->back().end = idx - 1;
+ } else if (lastColor == kSecondaryColor) {
+ secondaryRanges->back().end = idx - 1;
+ }
+
+ // We are starting a range. Which range?
+ // note: encode the x offset without the final 1 pixel border.
+ if (color == kPrimaryColor) {
+ primaryRanges->push_back(Range(idx - 1, length - 2));
+ } else if (color == kSecondaryColor) {
+ secondaryRanges->push_back(Range(idx - 1, length - 2));
+ }
+ lastColor = color;
+ }
+ }
+ return true;
+}
+
+/**
+ * Iterates over a row in an image. Implements the templated ImageLine interface.
+ */
+class HorizontalImageLine {
+public:
+ explicit HorizontalImageLine(uint8_t** rows, int32_t xOffset, int32_t yOffset,
+ int32_t length) :
+ mRows(rows), mXOffset(xOffset), mYOffset(yOffset), mLength(length) {
+ }
+
+ inline int32_t getLength() const {
+ return mLength;
+ }
+
+ inline uint32_t getColor(int32_t idx) const {
+ return NinePatch::packRGBA(mRows[mYOffset] + (idx + mXOffset) * 4);
+ }
+
+private:
+ uint8_t** mRows;
+ int32_t mXOffset, mYOffset, mLength;
+
+ DISALLOW_COPY_AND_ASSIGN(HorizontalImageLine);
+};
+
+/**
+ * Iterates over a column in an image. Implements the templated ImageLine interface.
+ */
+class VerticalImageLine {
+public:
+ explicit VerticalImageLine(uint8_t** rows, int32_t xOffset, int32_t yOffset,
+ int32_t length) :
+ mRows(rows), mXOffset(xOffset), mYOffset(yOffset), mLength(length) {
+ }
+
+ inline int32_t getLength() const {
+ return mLength;
+ }
+
+ inline uint32_t getColor(int32_t idx) const {
+ return NinePatch::packRGBA(mRows[mYOffset + idx] + (mXOffset * 4));
+ }
+
+private:
+ uint8_t** mRows;
+ int32_t mXOffset, mYOffset, mLength;
+
+ DISALLOW_COPY_AND_ASSIGN(VerticalImageLine);
+};
+
+class DiagonalImageLine {
+public:
+ explicit DiagonalImageLine(uint8_t** rows, int32_t xOffset, int32_t yOffset,
+ int32_t xStep, int32_t yStep, int32_t length) :
+ mRows(rows), mXOffset(xOffset), mYOffset(yOffset), mXStep(xStep), mYStep(yStep),
+ mLength(length) {
+ }
+
+ inline int32_t getLength() const {
+ return mLength;
+ }
+
+ inline uint32_t getColor(int32_t idx) const {
+ return NinePatch::packRGBA(
+ mRows[mYOffset + (idx * mYStep)] + ((idx + mXOffset) * mXStep) * 4);
+ }
+
+private:
+ uint8_t** mRows;
+ int32_t mXOffset, mYOffset, mXStep, mYStep, mLength;
+
+ DISALLOW_COPY_AND_ASSIGN(DiagonalImageLine);
+};
+
+class TransparentNeutralColorValidator : public ColorValidator {
+public:
+ bool isNeutralColor(uint32_t color) const override {
+ return getAlpha(color) == 0;
+ }
+};
+
+class WhiteNeutralColorValidator : public ColorValidator {
+public:
+ bool isNeutralColor(uint32_t color) const override {
+ return color == kColorOpaqueWhite;
+ }
+};
+
+inline static uint32_t getAlpha(uint32_t color) {
+ return (color & 0xff000000u) >> 24;
+}
+
+static bool populateBounds(const std::vector<Range>& padding,
+ const std::vector<Range>& layoutBounds,
+ const std::vector<Range>& stretchRegions,
+ const int32_t length,
+ int32_t* paddingStart, int32_t* paddingEnd,
+ int32_t* layoutStart, int32_t* layoutEnd,
+ const StringPiece& edgeName,
+ std::string* err) {
+ if (padding.size() > 1) {
+ std::stringstream errStream;
+ errStream << "too many padding sections on " << edgeName << " border";
+ *err = errStream.str();
+ return false;
+ }
+
+ *paddingStart = 0;
+ *paddingEnd = 0;
+ if (!padding.empty()) {
+ const Range& range = padding.front();
+ *paddingStart = range.start;
+ *paddingEnd = length - range.end;
+ } else if (!stretchRegions.empty()) {
+ // No padding was defined. Compute the padding from the first and last
+ // stretch regions.
+ *paddingStart = stretchRegions.front().start;
+ *paddingEnd = length - stretchRegions.back().end;
+ }
+
+ if (layoutBounds.size() > 2) {
+ std::stringstream errStream;
+ errStream << "too many layout bounds sections on " << edgeName << " border";
+ *err = errStream.str();
+ return false;
+ }
+
+ *layoutStart = 0;
+ *layoutEnd = 0;
+ if (layoutBounds.size() >= 1) {
+ const Range& range = layoutBounds.front();
+ // If there is only one layout bound segment, it might not start at 0, but then it should
+ // end at length.
+ if (range.start != 0 && range.end != length) {
+ std::stringstream errStream;
+ errStream << "layout bounds on " << edgeName << " border must start at edge";
+ *err = errStream.str();
+ return false;
+ }
+ *layoutStart = range.end;
+
+ if (layoutBounds.size() >= 2) {
+ const Range& range = layoutBounds.back();
+ if (range.end != length) {
+ std::stringstream errStream;
+ errStream << "layout bounds on " << edgeName << " border must start at edge";
+ *err = errStream.str();
+ return false;
+ }
+ *layoutEnd = length - range.start;
+ }
+ }
+ return true;
+}
+
+static int32_t calculateSegmentCount(const std::vector<Range>& stretchRegions, int32_t length) {
+ if (stretchRegions.size() == 0) {
+ return 0;
+ }
+
+ const bool startIsFixed = stretchRegions.front().start != 0;
+ const bool endIsFixed = stretchRegions.back().end != length;
+ int32_t modifier = 0;
+ if (startIsFixed && endIsFixed) {
+ modifier = 1;
+ } else if (!startIsFixed && !endIsFixed) {
+ modifier = -1;
+ }
+ return static_cast<int32_t>(stretchRegions.size()) * 2 + modifier;
+}
+
+static uint32_t getRegionColor(uint8_t** rows, const Bounds& region) {
+ // Sample the first pixel to compare against.
+ const uint32_t expectedColor = NinePatch::packRGBA(rows[region.top] + region.left * 4);
+ for (int32_t y = region.top; y < region.bottom; y++) {
+ const uint8_t* row = rows[y];
+ for (int32_t x = region.left; x < region.right; x++) {
+ const uint32_t color = NinePatch::packRGBA(row + x * 4);
+ if (getAlpha(color) == 0) {
+ // The color is transparent.
+ // If the expectedColor is not transparent, NO_COLOR.
+ if (getAlpha(expectedColor) != 0) {
+ return android::Res_png_9patch::NO_COLOR;
+ }
+ } else if (color != expectedColor) {
+ return android::Res_png_9patch::NO_COLOR;
+ }
+ }
+ }
+
+ if (getAlpha(expectedColor) == 0) {
+ return android::Res_png_9patch::TRANSPARENT_COLOR;
+ }
+ return expectedColor;
+}
+
+// Fills outColors with each 9-patch section's colour. If the whole section is transparent,
+// it gets the special TRANSPARENT colour. If the whole section is the same colour, it is assigned
+// that colour. Otherwise it gets the special NO_COLOR colour.
+//
+// Note that the rows contain the 9-patch 1px border, and the indices in the stretch regions are
+// already offset to exclude the border. This means that each time the rows are accessed,
+// the indices must be offset by 1.
+//
+// width and height also include the 9-patch 1px border.
+static void calculateRegionColors(uint8_t** rows,
+ const std::vector<Range>& horizontalStretchRegions,
+ const std::vector<Range>& verticalStretchRegions,
+ const int32_t width, const int32_t height,
+ std::vector<uint32_t>* outColors) {
+ int32_t nextTop = 0;
+ Bounds bounds;
+ auto rowIter = verticalStretchRegions.begin();
+ while (nextTop != height) {
+ if (rowIter != verticalStretchRegions.end()) {
+ if (nextTop != rowIter->start) {
+ // This is a fixed segment.
+ // Offset the bounds by 1 to accommodate the border.
+ bounds.top = nextTop + 1;
+ bounds.bottom = rowIter->start + 1;
+ nextTop = rowIter->start;
+ } else {
+ // This is a stretchy segment.
+ // Offset the bounds by 1 to accommodate the border.
+ bounds.top = rowIter->start + 1;
+ bounds.bottom = rowIter->end + 1;
+ nextTop = rowIter->end;
+ ++rowIter;
+ }
+ } else {
+ // This is the end, fixed section.
+ // Offset the bounds by 1 to accommodate the border.
+ bounds.top = nextTop + 1;
+ bounds.bottom = height + 1;
+ nextTop = height;
+ }
+
+ int32_t nextLeft = 0;
+ auto colIter = horizontalStretchRegions.begin();
+ while (nextLeft != width) {
+ if (colIter != horizontalStretchRegions.end()) {
+ if (nextLeft != colIter->start) {
+ // This is a fixed segment.
+ // Offset the bounds by 1 to accommodate the border.
+ bounds.left = nextLeft + 1;
+ bounds.right = colIter->start + 1;
+ nextLeft = colIter->start;
+ } else {
+ // This is a stretchy segment.
+ // Offset the bounds by 1 to accommodate the border.
+ bounds.left = colIter->start + 1;
+ bounds.right = colIter->end + 1;
+ nextLeft = colIter->end;
+ ++colIter;
+ }
+ } else {
+ // This is the end, fixed section.
+ // Offset the bounds by 1 to accommodate the border.
+ bounds.left = nextLeft + 1;
+ bounds.right = width + 1;
+ nextLeft = width;
+ }
+ outColors->push_back(getRegionColor(rows, bounds));
+ }
+ }
+}
+
+// Calculates the insets of a row/column of pixels based on where the largest alpha value begins
+// (on both sides).
+template <typename ImageLine>
+static void findOutlineInsets(const ImageLine* imageLine, int32_t* outStart, int32_t* outEnd) {
+ *outStart = 0;
+ *outEnd = 0;
+
+ const int32_t length = imageLine->getLength();
+ if (length < 3) {
+ return;
+ }
+
+ // If the length is odd, we want both sides to process the center pixel,
+ // so we use two different midpoints (to account for < and <= in the different loops).
+ const int32_t mid2 = length / 2;
+ const int32_t mid1 = mid2 + (length % 2);
+
+ uint32_t maxAlpha = 0;
+ for (int32_t i = 0; i < mid1 && maxAlpha != 0xff; i++) {
+ uint32_t alpha = getAlpha(imageLine->getColor(i));
+ if (alpha > maxAlpha) {
+ maxAlpha = alpha;
+ *outStart = i;
+ }
+ }
+
+ maxAlpha = 0;
+ for (int32_t i = length - 1; i >= mid2 && maxAlpha != 0xff; i--) {
+ uint32_t alpha = getAlpha(imageLine->getColor(i));
+ if (alpha > maxAlpha) {
+ maxAlpha = alpha;
+ *outEnd = length - (i + 1);
+ }
+ }
+ return;
+}
+
+template <typename ImageLine>
+static uint32_t findMaxAlpha(const ImageLine* imageLine) {
+ const int32_t length = imageLine->getLength();
+ uint32_t maxAlpha = 0;
+ for (int32_t idx = 0; idx < length && maxAlpha != 0xff; idx++) {
+ uint32_t alpha = getAlpha(imageLine->getColor(idx));
+ if (alpha > maxAlpha) {
+ maxAlpha = alpha;
+ }
+ }
+ return maxAlpha;
+}
+
+// Pack the pixels in as 0xAARRGGBB (as 9-patch expects it).
+uint32_t NinePatch::packRGBA(const uint8_t* pixel) {
+ return (pixel[3] << 24) | (pixel[0] << 16) | (pixel[1] << 8) | pixel[2];
+}
+
+std::unique_ptr<NinePatch> NinePatch::create(uint8_t** rows,
+ const int32_t width, const int32_t height,
+ std::string* err) {
+ if (width < 3 || height < 3) {
+ *err = "image must be at least 3x3 (1x1 image with 1 pixel border)";
+ return {};
+ }
+
+ std::vector<Range> horizontalPadding;
+ std::vector<Range> horizontalOpticalBounds;
+ std::vector<Range> verticalPadding;
+ std::vector<Range> verticalOpticalBounds;
+ std::vector<Range> unexpectedRanges;
+ std::unique_ptr<ColorValidator> colorValidator;
+
+ if (rows[0][3] == 0) {
+ colorValidator = util::make_unique<TransparentNeutralColorValidator>();
+ } else if (packRGBA(rows[0]) == kColorOpaqueWhite) {
+ colorValidator = util::make_unique<WhiteNeutralColorValidator>();
+ } else {
+ *err = "top-left corner pixel must be either opaque white or transparent";
+ return {};
+ }
+
+ // Private constructor, can't use make_unique.
+ auto ninePatch = std::unique_ptr<NinePatch>(new NinePatch());
+
+ HorizontalImageLine topRow(rows, 0, 0, width);
+ if (!fillRanges(&topRow, colorValidator.get(), &ninePatch->horizontalStretchRegions,
+ &unexpectedRanges, err)) {
+ return {};
+ }
+
+ if (!unexpectedRanges.empty()) {
+ const Range& range = unexpectedRanges[0];
+ std::stringstream errStream;
+ errStream << "found unexpected optical bounds (red pixel) on top border "
+ << "at x=" << range.start + 1;
+ *err = errStream.str();
+ return {};
+ }
+
+ VerticalImageLine leftCol(rows, 0, 0, height);
+ if (!fillRanges(&leftCol, colorValidator.get(), &ninePatch->verticalStretchRegions,
+ &unexpectedRanges, err)) {
+ return {};
+ }
+
+ if (!unexpectedRanges.empty()) {
+ const Range& range = unexpectedRanges[0];
+ std::stringstream errStream;
+ errStream << "found unexpected optical bounds (red pixel) on left border "
+ << "at y=" << range.start + 1;
+ return {};
+ }
+
+ HorizontalImageLine bottomRow(rows, 0, height - 1, width);
+ if (!fillRanges(&bottomRow, colorValidator.get(), &horizontalPadding,
+ &horizontalOpticalBounds, err)) {
+ return {};
+ }
+
+ if (!populateBounds(horizontalPadding, horizontalOpticalBounds,
+ ninePatch->horizontalStretchRegions, width - 2,
+ &ninePatch->padding.left, &ninePatch->padding.right,
+ &ninePatch->layoutBounds.left, &ninePatch->layoutBounds.right,
+ "bottom", err)) {
+ return {};
+ }
+
+ VerticalImageLine rightCol(rows, width - 1, 0, height);
+ if (!fillRanges(&rightCol, colorValidator.get(), &verticalPadding,
+ &verticalOpticalBounds, err)) {
+ return {};
+ }
+
+ if (!populateBounds(verticalPadding, verticalOpticalBounds,
+ ninePatch->verticalStretchRegions, height - 2,
+ &ninePatch->padding.top, &ninePatch->padding.bottom,
+ &ninePatch->layoutBounds.top, &ninePatch->layoutBounds.bottom,
+ "right", err)) {
+ return {};
+ }
+
+ // Fill the region colors of the 9-patch.
+ const int32_t numRows = calculateSegmentCount(ninePatch->horizontalStretchRegions, width - 2);
+ const int32_t numCols = calculateSegmentCount(ninePatch->verticalStretchRegions, height - 2);
+ if ((int64_t) numRows * (int64_t) numCols > 0x7f) {
+ *err = "too many regions in 9-patch";
+ return {};
+ }
+
+ ninePatch->regionColors.reserve(numRows * numCols);
+ calculateRegionColors(rows, ninePatch->horizontalStretchRegions,
+ ninePatch->verticalStretchRegions,
+ width - 2, height - 2,
+ &ninePatch->regionColors);
+
+ // Compute the outline based on opacity.
+
+ // Find left and right extent of 9-patch content on center row.
+ HorizontalImageLine midRow(rows, 1, height / 2, width - 2);
+ findOutlineInsets(&midRow, &ninePatch->outline.left, &ninePatch->outline.right);
+
+ // Find top and bottom extent of 9-patch content on center column.
+ VerticalImageLine midCol(rows, width / 2, 1, height - 2);
+ findOutlineInsets(&midCol, &ninePatch->outline.top, &ninePatch->outline.bottom);
+
+ const int32_t outlineWidth = (width - 2) - ninePatch->outline.left - ninePatch->outline.right;
+ const int32_t outlineHeight = (height - 2) - ninePatch->outline.top - ninePatch->outline.bottom;
+
+ // Find the largest alpha value within the outline area.
+ HorizontalImageLine outlineMidRow(rows,
+ 1 + ninePatch->outline.left,
+ 1 + ninePatch->outline.top + (outlineHeight / 2),
+ outlineWidth);
+ VerticalImageLine outlineMidCol(rows,
+ 1 + ninePatch->outline.left + (outlineWidth / 2),
+ 1 + ninePatch->outline.top,
+ outlineHeight);
+ ninePatch->outlineAlpha = std::max(findMaxAlpha(&outlineMidRow), findMaxAlpha(&outlineMidCol));
+
+ // Assuming the image is a round rect, compute the radius by marching
+ // diagonally from the top left corner towards the center.
+ DiagonalImageLine diagonal(rows, 1 + ninePatch->outline.left, 1 + ninePatch->outline.top,
+ 1, 1, std::min(outlineWidth, outlineHeight));
+ int32_t topLeft, bottomRight;
+ findOutlineInsets(&diagonal, &topLeft, &bottomRight);
+
+ /* Determine source radius based upon inset:
+ * sqrt(r^2 + r^2) = sqrt(i^2 + i^2) + r
+ * sqrt(2) * r = sqrt(2) * i + r
+ * (sqrt(2) - 1) * r = sqrt(2) * i
+ * r = sqrt(2) / (sqrt(2) - 1) * i
+ */
+ ninePatch->outlineRadius = 3.4142f * topLeft;
+ return ninePatch;
+}
+
+std::unique_ptr<uint8_t[]> NinePatch::serializeBase(size_t* outLen) const {
+ android::Res_png_9patch data;
+ data.numXDivs = static_cast<uint8_t>(horizontalStretchRegions.size()) * 2;
+ data.numYDivs = static_cast<uint8_t>(verticalStretchRegions.size()) * 2;
+ data.numColors = static_cast<uint8_t>(regionColors.size());
+ data.paddingLeft = padding.left;
+ data.paddingRight = padding.right;
+ data.paddingTop = padding.top;
+ data.paddingBottom = padding.bottom;
+
+ auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[data.serializedSize()]);
+ android::Res_png_9patch::serialize(data,
+ (const int32_t*) horizontalStretchRegions.data(),
+ (const int32_t*) verticalStretchRegions.data(),
+ regionColors.data(),
+ buffer.get());
+ // Convert to file endianness.
+ reinterpret_cast<android::Res_png_9patch*>(buffer.get())->deviceToFile();
+
+ *outLen = data.serializedSize();
+ return buffer;
+}
+
+std::unique_ptr<uint8_t[]> NinePatch::serializeLayoutBounds(size_t* outLen) const {
+ size_t chunkLen = sizeof(uint32_t) * 4;
+ auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[chunkLen]);
+ uint8_t* cursor = buffer.get();
+
+ memcpy(cursor, &layoutBounds.left, sizeof(layoutBounds.left));
+ cursor += sizeof(layoutBounds.left);
+
+ memcpy(cursor, &layoutBounds.top, sizeof(layoutBounds.top));
+ cursor += sizeof(layoutBounds.top);
+
+ memcpy(cursor, &layoutBounds.right, sizeof(layoutBounds.right));
+ cursor += sizeof(layoutBounds.right);
+
+ memcpy(cursor, &layoutBounds.bottom, sizeof(layoutBounds.bottom));
+ cursor += sizeof(layoutBounds.bottom);
+
+ *outLen = chunkLen;
+ return buffer;
+}
+
+std::unique_ptr<uint8_t[]> NinePatch::serializeRoundedRectOutline(size_t* outLen) const {
+ size_t chunkLen = sizeof(uint32_t) * 6;
+ auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[chunkLen]);
+ uint8_t* cursor = buffer.get();
+
+ memcpy(cursor, &outline.left, sizeof(outline.left));
+ cursor += sizeof(outline.left);
+
+ memcpy(cursor, &outline.top, sizeof(outline.top));
+ cursor += sizeof(outline.top);
+
+ memcpy(cursor, &outline.right, sizeof(outline.right));
+ cursor += sizeof(outline.right);
+
+ memcpy(cursor, &outline.bottom, sizeof(outline.bottom));
+ cursor += sizeof(outline.bottom);
+
+ *((float*) cursor) = outlineRadius;
+ cursor += sizeof(outlineRadius);
+
+ *((uint32_t*) cursor) = outlineAlpha;
+
+ *outLen = chunkLen;
+ return buffer;
+}
+
+::std::ostream& operator<<(::std::ostream& out, const Range& range) {
+ return out << "[" << range.start << ", " << range.end << ")";
+}
+
+::std::ostream& operator<<(::std::ostream& out, const Bounds& bounds) {
+ return out << "l=" << bounds.left
+ << " t=" << bounds.top
+ << " r=" << bounds.right
+ << " b=" << bounds.bottom;
+}
+
+::std::ostream& operator<<(::std::ostream& out, const NinePatch& ninePatch) {
+ return out << "horizontalStretch:" << util::joiner(ninePatch.horizontalStretchRegions, " ")
+ << " verticalStretch:" << util::joiner(ninePatch.verticalStretchRegions, " ")
+ << " padding: " << ninePatch.padding
+ << ", bounds: " << ninePatch.layoutBounds
+ << ", outline: " << ninePatch.outline
+ << " rad=" << ninePatch.outlineRadius
+ << " alpha=" << ninePatch.outlineAlpha;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/compile/NinePatch_test.cpp b/tools/aapt2/compile/NinePatch_test.cpp
new file mode 100644
index 0000000..3106ff8
--- /dev/null
+++ b/tools/aapt2/compile/NinePatch_test.cpp
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "compile/Image.h"
+#include "test/Test.h"
+
+namespace aapt {
+
+// Pixels are in RGBA_8888 packing.
+
+#define RED "\xff\x00\x00\xff"
+#define BLUE "\x00\x00\xff\xff"
+#define GREEN "\xff\x00\x00\xff"
+#define GR_70 "\xff\x00\x00\xb3"
+#define GR_50 "\xff\x00\x00\x80"
+#define GR_20 "\xff\x00\x00\x33"
+#define BLACK "\x00\x00\x00\xff"
+#define WHITE "\xff\xff\xff\xff"
+#define TRANS "\x00\x00\x00\x00"
+
+static uint8_t* k2x2[] = {
+ (uint8_t*) WHITE WHITE,
+ (uint8_t*) WHITE WHITE,
+};
+
+static uint8_t* kMixedNeutralColor3x3[] = {
+ (uint8_t*) WHITE BLACK TRANS,
+ (uint8_t*) TRANS RED TRANS,
+ (uint8_t*) WHITE WHITE WHITE,
+};
+
+static uint8_t* kTransparentNeutralColor3x3[] = {
+ (uint8_t*) TRANS BLACK TRANS,
+ (uint8_t*) BLACK RED BLACK,
+ (uint8_t*) TRANS BLACK TRANS,
+};
+
+static uint8_t* kSingleStretch7x6[] = {
+ (uint8_t*) WHITE WHITE BLACK BLACK BLACK WHITE WHITE,
+ (uint8_t*) WHITE RED RED RED RED RED WHITE,
+ (uint8_t*) BLACK RED RED RED RED RED WHITE,
+ (uint8_t*) BLACK RED RED RED RED RED WHITE,
+ (uint8_t*) WHITE RED RED RED RED RED WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
+};
+
+static uint8_t* kMultipleStretch10x7[] = {
+ (uint8_t*) WHITE WHITE BLACK WHITE BLACK BLACK WHITE BLACK WHITE WHITE,
+ (uint8_t*) BLACK RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
+ (uint8_t*) BLACK RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
+ (uint8_t*) WHITE RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
+ (uint8_t*) BLACK RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
+ (uint8_t*) BLACK RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
+};
+
+static uint8_t* kPadding6x5[] = {
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE BLACK,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*) WHITE WHITE BLACK BLACK WHITE WHITE,
+};
+
+static uint8_t* kLayoutBoundsWrongEdge3x3[] = {
+ (uint8_t*) WHITE RED WHITE,
+ (uint8_t*) RED WHITE WHITE,
+ (uint8_t*) WHITE WHITE WHITE,
+};
+
+static uint8_t* kLayoutBoundsNotEdgeAligned5x5[] = {
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE RED,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*) WHITE WHITE RED WHITE WHITE,
+};
+
+static uint8_t* kLayoutBounds5x5[] = {
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE RED,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE RED,
+ (uint8_t*) WHITE RED WHITE RED WHITE,
+};
+
+static uint8_t* kAsymmetricLayoutBounds5x5[] = {
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE RED,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*) WHITE RED WHITE WHITE WHITE,
+};
+
+static uint8_t* kPaddingAndLayoutBounds5x5[] = {
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE RED,
+ (uint8_t*) WHITE WHITE WHITE WHITE BLACK,
+ (uint8_t*) WHITE WHITE WHITE WHITE RED,
+ (uint8_t*) WHITE RED BLACK RED WHITE,
+};
+
+static uint8_t* kColorfulImage5x5[] = {
+ (uint8_t*) WHITE BLACK WHITE BLACK WHITE,
+ (uint8_t*) BLACK RED BLUE GREEN WHITE,
+ (uint8_t*) BLACK RED GREEN GREEN WHITE,
+ (uint8_t*) WHITE TRANS BLUE GREEN WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+};
+
+static uint8_t* kOutlineOpaque10x10[] = {
+ (uint8_t*) WHITE BLACK BLACK BLACK BLACK BLACK BLACK BLACK BLACK WHITE,
+ (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS GREEN GREEN GREEN GREEN TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS GREEN GREEN GREEN GREEN TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS GREEN GREEN GREEN GREEN TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS GREEN GREEN GREEN GREEN TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
+};
+
+static uint8_t* kOutlineTranslucent10x10[] = {
+ (uint8_t*) WHITE BLACK BLACK BLACK BLACK BLACK BLACK BLACK BLACK WHITE,
+ (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
+ (uint8_t*) WHITE TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
+};
+
+static uint8_t* kOutlineOffsetTranslucent12x10[] = {
+ (uint8_t*) WHITE WHITE WHITE BLACK BLACK BLACK BLACK BLACK BLACK BLACK BLACK WHITE,
+ (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
+ (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
+};
+
+static uint8_t* kOutlineRadius5x5[] = {
+ (uint8_t*) WHITE BLACK BLACK BLACK WHITE,
+ (uint8_t*) BLACK TRANS GREEN TRANS WHITE,
+ (uint8_t*) BLACK GREEN GREEN GREEN WHITE,
+ (uint8_t*) BLACK TRANS GREEN TRANS WHITE,
+ (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+};
+
+static uint8_t* kStretchAndPadding5x5[] = {
+ (uint8_t*) WHITE WHITE BLACK WHITE WHITE,
+ (uint8_t*) WHITE RED RED RED WHITE,
+ (uint8_t*) BLACK RED RED RED BLACK,
+ (uint8_t*) WHITE RED RED RED WHITE,
+ (uint8_t*) WHITE WHITE BLACK WHITE WHITE,
+};
+
+TEST(NinePatchTest, Minimum3x3) {
+ std::string err;
+ EXPECT_EQ(nullptr, NinePatch::create(k2x2, 2, 2, &err));
+ EXPECT_FALSE(err.empty());
+}
+
+TEST(NinePatchTest, MixedNeutralColors) {
+ std::string err;
+ EXPECT_EQ(nullptr, NinePatch::create(kMixedNeutralColor3x3, 3, 3, &err));
+ EXPECT_FALSE(err.empty());
+}
+
+TEST(NinePatchTest, TransparentNeutralColor) {
+ std::string err;
+ EXPECT_NE(nullptr, NinePatch::create(kTransparentNeutralColor3x3, 3, 3, &err));
+}
+
+TEST(NinePatchTest, SingleStretchRegion) {
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kSingleStretch7x6, 7, 6, &err);
+ ASSERT_NE(nullptr, ninePatch);
+
+ ASSERT_EQ(1u, ninePatch->horizontalStretchRegions.size());
+ ASSERT_EQ(1u, ninePatch->verticalStretchRegions.size());
+
+ EXPECT_EQ(Range(1, 4), ninePatch->horizontalStretchRegions.front());
+ EXPECT_EQ(Range(1, 3), ninePatch->verticalStretchRegions.front());
+}
+
+TEST(NinePatchTest, MultipleStretchRegions) {
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kMultipleStretch10x7, 10, 7, &err);
+ ASSERT_NE(nullptr, ninePatch);
+
+ ASSERT_EQ(3u, ninePatch->horizontalStretchRegions.size());
+ ASSERT_EQ(2u, ninePatch->verticalStretchRegions.size());
+
+ EXPECT_EQ(Range(1, 2), ninePatch->horizontalStretchRegions[0]);
+ EXPECT_EQ(Range(3, 5), ninePatch->horizontalStretchRegions[1]);
+ EXPECT_EQ(Range(6, 7), ninePatch->horizontalStretchRegions[2]);
+
+ EXPECT_EQ(Range(0, 2), ninePatch->verticalStretchRegions[0]);
+ EXPECT_EQ(Range(3, 5), ninePatch->verticalStretchRegions[1]);
+}
+
+TEST(NinePatchTest, InferPaddingFromStretchRegions) {
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kMultipleStretch10x7, 10, 7, &err);
+ ASSERT_NE(nullptr, ninePatch);
+ EXPECT_EQ(Bounds(1, 0, 1, 0), ninePatch->padding);
+}
+
+TEST(NinePatchTest, Padding) {
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kPadding6x5, 6, 5, &err);
+ ASSERT_NE(nullptr, ninePatch);
+ EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->padding);
+}
+
+TEST(NinePatchTest, LayoutBoundsAreOnWrongEdge) {
+ std::string err;
+ EXPECT_EQ(nullptr, NinePatch::create(kLayoutBoundsWrongEdge3x3, 3, 3, &err));
+ EXPECT_FALSE(err.empty());
+}
+
+TEST(NinePatchTest, LayoutBoundsMustTouchEdges) {
+ std::string err;
+ EXPECT_EQ(nullptr, NinePatch::create(kLayoutBoundsNotEdgeAligned5x5, 5, 5, &err));
+ EXPECT_FALSE(err.empty());
+}
+
+TEST(NinePatchTest, LayoutBounds) {
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kLayoutBounds5x5, 5, 5, &err);
+ ASSERT_NE(nullptr, ninePatch);
+ EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->layoutBounds);
+
+ ninePatch = NinePatch::create(kAsymmetricLayoutBounds5x5, 5, 5, &err);
+ ASSERT_NE(nullptr, ninePatch);
+ EXPECT_EQ(Bounds(1, 1, 0, 0), ninePatch->layoutBounds);
+}
+
+TEST(NinePatchTest, PaddingAndLayoutBounds) {
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kPaddingAndLayoutBounds5x5, 5, 5,
+ &err);
+ ASSERT_NE(nullptr, ninePatch);
+ EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->padding);
+ EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->layoutBounds);
+}
+
+TEST(NinePatchTest, RegionColorsAreCorrect) {
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kColorfulImage5x5, 5, 5, &err);
+ ASSERT_NE(nullptr, ninePatch);
+
+ std::vector<uint32_t> expectedColors = {
+ NinePatch::packRGBA((uint8_t*) RED),
+ (uint32_t) android::Res_png_9patch::NO_COLOR,
+ NinePatch::packRGBA((uint8_t*) GREEN),
+ (uint32_t) android::Res_png_9patch::TRANSPARENT_COLOR,
+ NinePatch::packRGBA((uint8_t*) BLUE),
+ NinePatch::packRGBA((uint8_t*) GREEN),
+ };
+ EXPECT_EQ(expectedColors, ninePatch->regionColors);
+}
+
+TEST(NinePatchTest, OutlineFromOpaqueImage) {
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kOutlineOpaque10x10, 10, 10, &err);
+ ASSERT_NE(nullptr, ninePatch);
+ EXPECT_EQ(Bounds(2, 2, 2, 2), ninePatch->outline);
+ EXPECT_EQ(0x000000ffu, ninePatch->outlineAlpha);
+ EXPECT_EQ(0.0f, ninePatch->outlineRadius);
+}
+
+TEST(NinePatchTest, OutlineFromTranslucentImage) {
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kOutlineTranslucent10x10, 10, 10,
+ &err);
+ ASSERT_NE(nullptr, ninePatch);
+ EXPECT_EQ(Bounds(3, 3, 3, 3), ninePatch->outline);
+ EXPECT_EQ(0x000000b3u, ninePatch->outlineAlpha);
+ EXPECT_EQ(0.0f, ninePatch->outlineRadius);
+}
+
+TEST(NinePatchTest, OutlineFromOffCenterImage) {
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kOutlineOffsetTranslucent12x10, 12, 10,
+ &err);
+ ASSERT_NE(nullptr, ninePatch);
+
+ // TODO(adamlesinski): The old AAPT algorithm searches from the outside to the middle
+ // for each inset. If the outline is shifted, the search may not find a closer bounds.
+ // This check should be:
+ // EXPECT_EQ(Bounds(5, 3, 3, 3), ninePatch->outline);
+ // but until I know what behaviour I'm breaking, I will leave it at the incorrect:
+ EXPECT_EQ(Bounds(4, 3, 3, 3), ninePatch->outline);
+
+ EXPECT_EQ(0x000000b3u, ninePatch->outlineAlpha);
+ EXPECT_EQ(0.0f, ninePatch->outlineRadius);
+}
+
+TEST(NinePatchTest, OutlineRadius) {
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kOutlineRadius5x5, 5, 5, &err);
+ ASSERT_NE(nullptr, ninePatch);
+ EXPECT_EQ(Bounds(0, 0, 0, 0), ninePatch->outline);
+ EXPECT_EQ(3.4142f, ninePatch->outlineRadius);
+}
+
+::testing::AssertionResult bigEndianOne(uint8_t* cursor) {
+ if (cursor[0] == 0 && cursor[1] == 0 && cursor[2] == 0 && cursor[3] == 1) {
+ return ::testing::AssertionSuccess();
+ }
+ return ::testing::AssertionFailure() << "Not BigEndian 1";
+}
+
+TEST(NinePatchTest, SerializePngEndianness) {
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kStretchAndPadding5x5, 5, 5, &err);
+ ASSERT_NE(nullptr, ninePatch);
+
+ size_t len;
+ std::unique_ptr<uint8_t[]> data = ninePatch->serializeBase(&len);
+ ASSERT_NE(nullptr, data);
+ ASSERT_NE(0u, len);
+
+ // Skip past wasDeserialized + numXDivs + numYDivs + numColors + xDivsOffset + yDivsOffset
+ // (12 bytes)
+ uint8_t* cursor = data.get() + 12;
+
+ // Check that padding is big-endian. Expecting value 1.
+ EXPECT_TRUE(bigEndianOne(cursor));
+ EXPECT_TRUE(bigEndianOne(cursor + 4));
+ EXPECT_TRUE(bigEndianOne(cursor + 8));
+ EXPECT_TRUE(bigEndianOne(cursor + 12));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/compile/Png.h b/tools/aapt2/compile/Png.h
index f835b06e..4a15d95 100644
--- a/tools/aapt2/compile/Png.h
+++ b/tools/aapt2/compile/Png.h
@@ -17,10 +17,14 @@
#ifndef AAPT_PNG_H
#define AAPT_PNG_H
-#include "util/BigBuffer.h"
#include "Diagnostics.h"
#include "Source.h"
+#include "compile/Image.h"
+#include "io/Io.h"
+#include "process/IResourceTableConsumer.h"
+#include "util/BigBuffer.h"
+#include <android-base/macros.h>
#include <iostream>
#include <string>
@@ -40,8 +44,51 @@
private:
IDiagnostics* mDiag;
+
+ DISALLOW_COPY_AND_ASSIGN(Png);
};
+/**
+ * An InputStream that filters out unimportant PNG chunks.
+ */
+class PngChunkFilter : public io::InputStream {
+public:
+ explicit PngChunkFilter(const StringPiece& data);
+
+ bool Next(const void** buffer, int* len) override;
+ void BackUp(int count) override;
+ bool Skip(int count) override;
+
+ int64_t ByteCount() const override {
+ return static_cast<int64_t>(mWindowStart);
+ }
+
+ bool HadError() const override {
+ return mError;
+ }
+
+private:
+ bool consumeWindow(const void** buffer, int* len);
+
+ StringPiece mData;
+ size_t mWindowStart = 0;
+ size_t mWindowEnd = 0;
+ bool mError = false;
+
+ DISALLOW_COPY_AND_ASSIGN(PngChunkFilter);
+};
+
+/**
+ * Reads a PNG from the InputStream into memory as an RGBA Image.
+ */
+std::unique_ptr<Image> readPng(IAaptContext* context, io::InputStream* in);
+
+/**
+ * Writes the RGBA Image, with optional 9-patch meta-data, into the OutputStream as a PNG.
+ */
+bool writePng(IAaptContext* context, const Image* image, const NinePatch* ninePatch,
+ io::OutputStream* out, const PngOptions& options);
+
} // namespace aapt
#endif // AAPT_PNG_H
diff --git a/tools/aapt2/compile/PngChunkFilter.cpp b/tools/aapt2/compile/PngChunkFilter.cpp
new file mode 100644
index 0000000..70a881f
--- /dev/null
+++ b/tools/aapt2/compile/PngChunkFilter.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "compile/Png.h"
+#include "io/Io.h"
+#include "util/StringPiece.h"
+
+namespace aapt {
+
+static constexpr const char* kPngSignature = "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a";
+
+// Useful helper function that encodes individual bytes into a uint32
+// at compile time.
+constexpr uint32_t u32(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
+ return (((uint32_t) a) << 24)
+ | (((uint32_t) b) << 16)
+ | (((uint32_t) c) << 8)
+ | ((uint32_t) d);
+}
+
+// Whitelist of PNG chunk types that we want to keep in the resulting PNG.
+enum PngChunkTypes {
+ kPngChunkIHDR = u32(73, 72, 68, 82),
+ kPngChunkIDAT = u32(73, 68, 65, 84),
+ kPngChunkIEND = u32(73, 69, 78, 68),
+ kPngChunkPLTE = u32(80, 76, 84, 69),
+ kPngChunktRNS = u32(116, 82, 78, 83),
+ kPngChunksRGB = u32(115, 82, 71, 66),
+};
+
+static uint32_t peek32LE(const char* data) {
+ uint32_t word = ((uint32_t) data[0]) & 0x000000ff;
+ word <<= 8;
+ word |= ((uint32_t) data[1]) & 0x000000ff;
+ word <<= 8;
+ word |= ((uint32_t) data[2]) & 0x000000ff;
+ word <<= 8;
+ word |= ((uint32_t) data[3]) & 0x000000ff;
+ return word;
+}
+
+static bool isPngChunkWhitelisted(uint32_t type) {
+ switch (type) {
+ case kPngChunkIHDR:
+ case kPngChunkIDAT:
+ case kPngChunkIEND:
+ case kPngChunkPLTE:
+ case kPngChunktRNS:
+ case kPngChunksRGB:
+ return true;
+ default:
+ return false;
+ }
+}
+
+PngChunkFilter::PngChunkFilter(const StringPiece& data) : mData(data) {
+ if (util::stringStartsWith(mData, kPngSignature)) {
+ mWindowStart = 0;
+ mWindowEnd = strlen(kPngSignature);
+ } else {
+ mError = true;
+ }
+}
+
+bool PngChunkFilter::consumeWindow(const void** buffer, int* len) {
+ if (mWindowStart != mWindowEnd) {
+ // We have bytes to give from our window.
+ const int bytesRead = (int) (mWindowEnd - mWindowStart);
+ *buffer = mData.data() + mWindowStart;
+ *len = bytesRead;
+ mWindowStart = mWindowEnd;
+ return true;
+ }
+ return false;
+}
+
+bool PngChunkFilter::Next(const void** buffer, int* len) {
+ if (mError) {
+ return false;
+ }
+
+ // In case BackUp was called, we must consume the window.
+ if (consumeWindow(buffer, len)) {
+ return true;
+ }
+
+ // Advance the window as far as possible (until we meet a chunk that
+ // we want to strip).
+ while (mWindowEnd < mData.size()) {
+ // Chunk length (4 bytes) + type (4 bytes) + crc32 (4 bytes) = 12 bytes.
+ const size_t kMinChunkHeaderSize = 3 * sizeof(uint32_t);
+
+ // Is there enough room for a chunk header?
+ if (mData.size() - mWindowStart < kMinChunkHeaderSize) {
+ mError = true;
+ return false;
+ }
+
+ // Verify the chunk length.
+ const uint32_t chunkLen = peek32LE(mData.data() + mWindowEnd);
+ if (((uint64_t) chunkLen) + ((uint64_t) mWindowEnd) + sizeof(uint32_t) > mData.size()) {
+ // Overflow.
+ mError = true;
+ return false;
+ }
+
+ // Do we strip this chunk?
+ const uint32_t chunkType = peek32LE(mData.data() + mWindowEnd + sizeof(uint32_t));
+ if (isPngChunkWhitelisted(chunkType)) {
+ // Advance the window to include this chunk.
+ mWindowEnd += kMinChunkHeaderSize + chunkLen;
+ } else {
+ // We want to strip this chunk. If we accumulated a window,
+ // we must return the window now.
+ if (mWindowStart != mWindowEnd) {
+ break;
+ }
+
+ // The window is empty, so we can advance past this chunk
+ // and keep looking for the next good chunk,
+ mWindowEnd += kMinChunkHeaderSize + chunkLen;
+ mWindowStart = mWindowEnd;
+ }
+ }
+
+ if (consumeWindow(buffer, len)) {
+ return true;
+ }
+ return false;
+}
+
+void PngChunkFilter::BackUp(int count) {
+ if (mError) {
+ return;
+ }
+ mWindowStart -= count;
+}
+
+bool PngChunkFilter::Skip(int count) {
+ if (mError) {
+ return false;
+ }
+
+ const void* buffer;
+ int len;
+ while (count > 0) {
+ if (!Next(&buffer, &len)) {
+ return false;
+ }
+ if (len > count) {
+ BackUp(len - count);
+ count = 0;
+ } else {
+ count -= len;
+ }
+ }
+ return true;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/compile/PngCrunch.cpp b/tools/aapt2/compile/PngCrunch.cpp
new file mode 100644
index 0000000..a2e3f4f
--- /dev/null
+++ b/tools/aapt2/compile/PngCrunch.cpp
@@ -0,0 +1,724 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "compile/Png.h"
+
+#include <algorithm>
+#include <android-base/errors.h>
+#include <android-base/macros.h>
+#include <png.h>
+#include <unordered_map>
+#include <unordered_set>
+#include <zlib.h>
+
+namespace aapt {
+
+// Size in bytes of the PNG signature.
+constexpr size_t kPngSignatureSize = 8u;
+
+/**
+ * Custom deleter that destroys libpng read and info structs.
+ */
+class PngReadStructDeleter {
+public:
+ explicit PngReadStructDeleter(png_structp readPtr, png_infop infoPtr) :
+ mReadPtr(readPtr), mInfoPtr(infoPtr) {
+ }
+
+ ~PngReadStructDeleter() {
+ png_destroy_read_struct(&mReadPtr, &mInfoPtr, nullptr);
+ }
+
+private:
+ png_structp mReadPtr;
+ png_infop mInfoPtr;
+
+ DISALLOW_COPY_AND_ASSIGN(PngReadStructDeleter);
+};
+
+/**
+ * Custom deleter that destroys libpng write and info structs.
+ */
+class PngWriteStructDeleter {
+public:
+ explicit PngWriteStructDeleter(png_structp writePtr, png_infop infoPtr) :
+ mWritePtr(writePtr), mInfoPtr(infoPtr) {
+ }
+
+ ~PngWriteStructDeleter() {
+ png_destroy_write_struct(&mWritePtr, &mInfoPtr);
+ }
+
+private:
+ png_structp mWritePtr;
+ png_infop mInfoPtr;
+
+ DISALLOW_COPY_AND_ASSIGN(PngWriteStructDeleter);
+};
+
+// Custom warning logging method that uses IDiagnostics.
+static void logWarning(png_structp pngPtr, png_const_charp warningMsg) {
+ IDiagnostics* diag = (IDiagnostics*) png_get_error_ptr(pngPtr);
+ diag->warn(DiagMessage() << warningMsg);
+}
+
+// Custom error logging method that uses IDiagnostics.
+static void logError(png_structp pngPtr, png_const_charp errorMsg) {
+ IDiagnostics* diag = (IDiagnostics*) png_get_error_ptr(pngPtr);
+ diag->error(DiagMessage() << errorMsg);
+}
+
+static void readDataFromStream(png_structp pngPtr, png_bytep buffer, png_size_t len) {
+ io::InputStream* in = (io::InputStream*) png_get_io_ptr(pngPtr);
+
+ const void* inBuffer;
+ int inLen;
+ if (!in->Next(&inBuffer, &inLen)) {
+ if (in->HadError()) {
+ std::string err = in->GetError();
+ png_error(pngPtr, err.c_str());
+ }
+ return;
+ }
+
+ const size_t bytesRead = std::min(static_cast<size_t>(inLen), len);
+ memcpy(buffer, inBuffer, bytesRead);
+ if (bytesRead != static_cast<size_t>(inLen)) {
+ in->BackUp(inLen - static_cast<int>(bytesRead));
+ }
+}
+
+static void writeDataToStream(png_structp pngPtr, png_bytep buffer, png_size_t len) {
+ io::OutputStream* out = (io::OutputStream*) png_get_io_ptr(pngPtr);
+
+ void* outBuffer;
+ int outLen;
+ while (len > 0) {
+ if (!out->Next(&outBuffer, &outLen)) {
+ if (out->HadError()) {
+ std::string err = out->GetError();
+ png_error(pngPtr, err.c_str());
+ }
+ return;
+ }
+
+ const size_t bytesWritten = std::min(static_cast<size_t>(outLen), len);
+ memcpy(outBuffer, buffer, bytesWritten);
+
+ // Advance the input buffer.
+ buffer += bytesWritten;
+ len -= bytesWritten;
+
+ // Advance the output buffer.
+ outLen -= static_cast<int>(bytesWritten);
+ }
+
+ // If the entire output buffer wasn't used, backup.
+ if (outLen > 0) {
+ out->BackUp(outLen);
+ }
+}
+
+std::unique_ptr<Image> readPng(IAaptContext* context, io::InputStream* in) {
+ // Read the first 8 bytes of the file looking for the PNG signature.
+ // Bail early if it does not match.
+ const png_byte* signature;
+ int bufferSize;
+ if (!in->Next((const void**) &signature, &bufferSize)) {
+ context->getDiagnostics()->error(DiagMessage()
+ << android::base::SystemErrorCodeToString(errno));
+ return {};
+ }
+
+ if (static_cast<size_t>(bufferSize) < kPngSignatureSize
+ || png_sig_cmp(signature, 0, kPngSignatureSize) != 0) {
+ context->getDiagnostics()->error(DiagMessage()
+ << "file signature does not match PNG signature");
+ return {};
+ }
+
+ // Start at the beginning of the first chunk.
+ in->BackUp(bufferSize - static_cast<int>(kPngSignatureSize));
+
+ // Create and initialize the png_struct with the default error and warning handlers.
+ // The header version is also passed in to ensure that this was built against the same
+ // version of libpng.
+ png_structp readPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
+ if (readPtr == nullptr) {
+ context->getDiagnostics()->error(DiagMessage()
+ << "failed to create libpng read png_struct");
+ return {};
+ }
+
+ // Create and initialize the memory for image header and data.
+ png_infop infoPtr = png_create_info_struct(readPtr);
+ if (infoPtr == nullptr) {
+ context->getDiagnostics()->error(DiagMessage() << "failed to create libpng read png_info");
+ png_destroy_read_struct(&readPtr, nullptr, nullptr);
+ return {};
+ }
+
+ // Automatically release PNG resources at end of scope.
+ PngReadStructDeleter pngReadDeleter(readPtr, infoPtr);
+
+ // libpng uses longjmp to jump to an error handling routine.
+ // setjmp will only return true if it was jumped to, aka there was
+ // an error.
+ if (setjmp(png_jmpbuf(readPtr))) {
+ return {};
+ }
+
+ // Handle warnings ourselves via IDiagnostics.
+ png_set_error_fn(readPtr, (png_voidp) context->getDiagnostics(), logError, logWarning);
+
+ // Set up the read functions which read from our custom data sources.
+ png_set_read_fn(readPtr, (png_voidp) in, readDataFromStream);
+
+ // Skip the signature that we already read.
+ png_set_sig_bytes(readPtr, kPngSignatureSize);
+
+ // Read the chunk headers.
+ png_read_info(readPtr, infoPtr);
+
+ // Extract image meta-data from the various chunk headers.
+ uint32_t width, height;
+ int bitDepth, colorType, interlaceMethod, compressionMethod, filterMethod;
+ png_get_IHDR(readPtr, infoPtr, &width, &height, &bitDepth, &colorType, &interlaceMethod,
+ &compressionMethod, &filterMethod);
+
+ // When the image is read, expand it so that it is in RGBA 8888 format
+ // so that image handling is uniform.
+
+ if (colorType == PNG_COLOR_TYPE_PALETTE) {
+ png_set_palette_to_rgb(readPtr);
+ }
+
+ if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
+ png_set_expand_gray_1_2_4_to_8(readPtr);
+ }
+
+ if (png_get_valid(readPtr, infoPtr, PNG_INFO_tRNS)) {
+ png_set_tRNS_to_alpha(readPtr);
+ }
+
+ if (bitDepth == 16) {
+ png_set_strip_16(readPtr);
+ }
+
+ if (!(colorType & PNG_COLOR_MASK_ALPHA)) {
+ png_set_add_alpha(readPtr, 0xFF, PNG_FILLER_AFTER);
+ }
+
+ if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
+ png_set_gray_to_rgb(readPtr);
+ }
+
+ if (interlaceMethod != PNG_INTERLACE_NONE) {
+ png_set_interlace_handling(readPtr);
+ }
+
+ // Once all the options for reading have been set, we need to flush
+ // them to libpng.
+ png_read_update_info(readPtr, infoPtr);
+
+ // 9-patch uses int32_t to index images, so we cap the image dimensions to something
+ // that can always be represented by 9-patch.
+ if (width > std::numeric_limits<int32_t>::max() ||
+ height > std::numeric_limits<int32_t>::max()) {
+ context->getDiagnostics()->error(DiagMessage() << "PNG image dimensions are too large: "
+ << width << "x" << height);
+ return {};
+ }
+
+ std::unique_ptr<Image> outputImage = util::make_unique<Image>();
+ outputImage->width = static_cast<int32_t>(width);
+ outputImage->height = static_cast<int32_t>(height);
+
+ const size_t rowBytes = png_get_rowbytes(readPtr, infoPtr);
+ assert(rowBytes == 4 * width); // RGBA
+
+ // Allocate one large block to hold the image.
+ outputImage->data = std::unique_ptr<uint8_t[]>(new uint8_t[height * rowBytes]);
+
+ // Create an array of rows that index into the data block.
+ outputImage->rows = std::unique_ptr<uint8_t*[]>(new uint8_t*[height]);
+ for (uint32_t h = 0; h < height; h++) {
+ outputImage->rows[h] = outputImage->data.get() + (h * rowBytes);
+ }
+
+ // Actually read the image pixels.
+ png_read_image(readPtr, outputImage->rows.get());
+
+ // Finish reading. This will read any other chunks after the image data.
+ png_read_end(readPtr, infoPtr);
+
+ return outputImage;
+}
+
+/**
+ * Experimentally chosen constant to be added to the overhead of using color type
+ * PNG_COLOR_TYPE_PALETTE to account for the uncompressability of the palette chunk.
+ * Without this, many small PNGs encoded with palettes are larger after compression than
+ * the same PNGs encoded as RGBA.
+ */
+constexpr static const size_t kPaletteOverheadConstant = 1024u * 10u;
+
+// Pick a color type by which to encode the image, based on which color type will take
+// the least amount of disk space.
+//
+// 9-patch images traditionally have not been encoded with palettes.
+// The original rationale was to avoid dithering until after scaling,
+// but I don't think this would be an issue with palettes. Either way,
+// our naive size estimation tends to be wrong for small images like 9-patches
+// and using palettes balloons the size of the resulting 9-patch.
+// In order to not regress in size, restrict 9-patch to not use palettes.
+
+// The options are:
+//
+// - RGB
+// - RGBA
+// - RGB + cheap alpha
+// - Color palette
+// - Color palette + cheap alpha
+// - Color palette + alpha palette
+// - Grayscale
+// - Grayscale + cheap alpha
+// - Grayscale + alpha
+//
+static int pickColorType(int32_t width, int32_t height,
+ bool grayScale, bool convertibleToGrayScale, bool hasNinePatch,
+ size_t colorPaletteSize, size_t alphaPaletteSize) {
+ const size_t paletteChunkSize = 16 + colorPaletteSize * 3;
+ const size_t alphaChunkSize = 16 + alphaPaletteSize;
+ const size_t colorAlphaDataChunkSize = 16 + 4 * width * height;
+ const size_t colorDataChunkSize = 16 + 3 * width * height;
+ const size_t grayScaleAlphaDataChunkSize = 16 + 2 * width * height;
+ const size_t paletteDataChunkSize = 16 + width * height;
+
+ if (grayScale) {
+ if (alphaPaletteSize == 0) {
+ // This is the smallest the data can be.
+ return PNG_COLOR_TYPE_GRAY;
+ } else if (colorPaletteSize <= 256 && !hasNinePatch) {
+ // This grayscale has alpha and can fit within a palette.
+ // See if it is worth fitting into a palette.
+ const size_t paletteThreshold = paletteChunkSize + alphaChunkSize +
+ paletteDataChunkSize + kPaletteOverheadConstant;
+ if (grayScaleAlphaDataChunkSize > paletteThreshold) {
+ return PNG_COLOR_TYPE_PALETTE;
+ }
+ }
+ return PNG_COLOR_TYPE_GRAY_ALPHA;
+ }
+
+
+ if (colorPaletteSize <= 256 && !hasNinePatch) {
+ // This image can fit inside a palette. Let's see if it is worth it.
+ size_t totalSizeWithPalette = paletteDataChunkSize + paletteChunkSize;
+ size_t totalSizeWithoutPalette = colorDataChunkSize;
+ if (alphaPaletteSize > 0) {
+ totalSizeWithPalette += alphaPaletteSize;
+ totalSizeWithoutPalette = colorAlphaDataChunkSize;
+ }
+
+ if (totalSizeWithoutPalette > totalSizeWithPalette + kPaletteOverheadConstant) {
+ return PNG_COLOR_TYPE_PALETTE;
+ }
+ }
+
+ if (convertibleToGrayScale) {
+ if (alphaPaletteSize == 0) {
+ return PNG_COLOR_TYPE_GRAY;
+ } else {
+ return PNG_COLOR_TYPE_GRAY_ALPHA;
+ }
+ }
+
+ if (alphaPaletteSize == 0) {
+ return PNG_COLOR_TYPE_RGB;
+ }
+ return PNG_COLOR_TYPE_RGBA;
+}
+
+// Assigns indices to the color and alpha palettes, encodes them, and then invokes
+// png_set_PLTE/png_set_tRNS.
+// This must be done before writing image data.
+// Image data must be transformed to use the indices assigned within the palette.
+static void writePalette(png_structp writePtr, png_infop writeInfoPtr,
+ std::unordered_map<uint32_t, int>* colorPalette,
+ std::unordered_set<uint32_t>* alphaPalette) {
+ assert(colorPalette->size() <= 256);
+ assert(alphaPalette->size() <= 256);
+
+ // Populate the PNG palette struct and assign indices to the color
+ // palette.
+
+ // Colors in the alpha palette should have smaller indices.
+ // This will ensure that we can truncate the alpha palette if it is
+ // smaller than the color palette.
+ int index = 0;
+ for (uint32_t color : *alphaPalette) {
+ (*colorPalette)[color] = index++;
+ }
+
+ // Assign the rest of the entries.
+ for (auto& entry : *colorPalette) {
+ if (entry.second == -1) {
+ entry.second = index++;
+ }
+ }
+
+ // Create the PNG color palette struct.
+ auto colorPaletteBytes = std::unique_ptr<png_color[]>(new png_color[colorPalette->size()]);
+
+ std::unique_ptr<png_byte[]> alphaPaletteBytes;
+ if (!alphaPalette->empty()) {
+ alphaPaletteBytes = std::unique_ptr<png_byte[]>(new png_byte[alphaPalette->size()]);
+ }
+
+ for (const auto& entry : *colorPalette) {
+ const uint32_t color = entry.first;
+ const int index = entry.second;
+ assert(index >= 0);
+ assert(static_cast<size_t>(index) < colorPalette->size());
+
+ png_colorp slot = colorPaletteBytes.get() + index;
+ slot->red = color >> 24;
+ slot->green = color >> 16;
+ slot->blue = color >> 8;
+
+ const png_byte alpha = color & 0x000000ff;
+ if (alpha != 0xff && alphaPaletteBytes) {
+ assert(static_cast<size_t>(index) < alphaPalette->size());
+ alphaPaletteBytes[index] = alpha;
+ }
+ }
+
+ // The bytes get copied here, so it is safe to release colorPaletteBytes at the end of function
+ // scope.
+ png_set_PLTE(writePtr, writeInfoPtr, colorPaletteBytes.get(), colorPalette->size());
+
+ if (alphaPaletteBytes) {
+ png_set_tRNS(writePtr, writeInfoPtr, alphaPaletteBytes.get(), alphaPalette->size(),
+ nullptr);
+ }
+}
+
+// Write the 9-patch custom PNG chunks to writeInfoPtr. This must be done before
+// writing image data.
+static void writeNinePatch(png_structp writePtr, png_infop writeInfoPtr,
+ const NinePatch* ninePatch) {
+ // The order of the chunks is important.
+ // 9-patch code in older platforms expects the 9-patch chunk to
+ // be last.
+
+ png_unknown_chunk unknownChunks[3];
+ memset(unknownChunks, 0, sizeof(unknownChunks));
+
+ size_t index = 0;
+ size_t chunkLen = 0;
+
+ std::unique_ptr<uint8_t[]> serializedOutline =
+ ninePatch->serializeRoundedRectOutline(&chunkLen);
+ strcpy((char*) unknownChunks[index].name, "npOl");
+ unknownChunks[index].size = chunkLen;
+ unknownChunks[index].data = (png_bytep) serializedOutline.get();
+ unknownChunks[index].location = PNG_HAVE_PLTE;
+ index++;
+
+ std::unique_ptr<uint8_t[]> serializedLayoutBounds;
+ if (ninePatch->layoutBounds.nonZero()) {
+ serializedLayoutBounds = ninePatch->serializeLayoutBounds(&chunkLen);
+ strcpy((char*) unknownChunks[index].name, "npLb");
+ unknownChunks[index].size = chunkLen;
+ unknownChunks[index].data = (png_bytep) serializedLayoutBounds.get();
+ unknownChunks[index].location = PNG_HAVE_PLTE;
+ index++;
+ }
+
+ std::unique_ptr<uint8_t[]> serializedNinePatch = ninePatch->serializeBase(&chunkLen);
+ strcpy((char*) unknownChunks[index].name, "npTc");
+ unknownChunks[index].size = chunkLen;
+ unknownChunks[index].data = (png_bytep) serializedNinePatch.get();
+ unknownChunks[index].location = PNG_HAVE_PLTE;
+ index++;
+
+ // Handle all unknown chunks. We are manually setting the chunks here,
+ // so we will only ever handle our custom chunks.
+ png_set_keep_unknown_chunks(writePtr, PNG_HANDLE_CHUNK_ALWAYS, nullptr, 0);
+
+ // Set the actual chunks here. The data gets copied, so our buffers can
+ // safely go out of scope.
+ png_set_unknown_chunks(writePtr, writeInfoPtr, unknownChunks, index);
+}
+
+bool writePng(IAaptContext* context, const Image* image, const NinePatch* ninePatch,
+ io::OutputStream* out, const PngOptions& options) {
+ // Create and initialize the write png_struct with the default error and warning handlers.
+ // The header version is also passed in to ensure that this was built against the same
+ // version of libpng.
+ png_structp writePtr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
+ nullptr, nullptr, nullptr);
+ if (writePtr == nullptr) {
+ context->getDiagnostics()->error(DiagMessage()
+ << "failed to create libpng write png_struct");
+ return false;
+ }
+
+ // Allocate memory to store image header data.
+ png_infop writeInfoPtr = png_create_info_struct(writePtr);
+ if (writeInfoPtr == nullptr) {
+ context->getDiagnostics()->error(DiagMessage() << "failed to create libpng write png_info");
+ png_destroy_write_struct(&writePtr, nullptr);
+ return false;
+ }
+
+ // Automatically release PNG resources at end of scope.
+ PngWriteStructDeleter pngWriteDeleter(writePtr, writeInfoPtr);
+
+ // libpng uses longjmp to jump to error handling routines.
+ // setjmp will return true only if it was jumped to, aka, there was an error.
+ if (setjmp(png_jmpbuf(writePtr))) {
+ return false;
+ }
+
+ // Handle warnings with our IDiagnostics.
+ png_set_error_fn(writePtr, (png_voidp) context->getDiagnostics(), logError, logWarning);
+
+ // Set up the write functions which write to our custom data sources.
+ png_set_write_fn(writePtr, (png_voidp) out, writeDataToStream, nullptr);
+
+ // We want small files and can take the performance hit to achieve this goal.
+ png_set_compression_level(writePtr, Z_BEST_COMPRESSION);
+
+ // Begin analysis of the image data.
+ // Scan the entire image and determine if:
+ // 1. Every pixel has R == G == B (grayscale)
+ // 2. Every pixel has A == 255 (opaque)
+ // 3. There are no more than 256 distinct RGBA colors (palette).
+ std::unordered_map<uint32_t, int> colorPalette;
+ std::unordered_set<uint32_t> alphaPalette;
+ bool needsToZeroRGBChannelsOfTransparentPixels = false;
+ bool grayScale = true;
+ int maxGrayDeviation = 0;
+
+ for (int32_t y = 0; y < image->height; y++) {
+ const uint8_t* row = image->rows[y];
+ for (int32_t x = 0; x < image->width; x++) {
+ int red = *row++;
+ int green = *row++;
+ int blue = *row++;
+ int alpha = *row++;
+
+ if (alpha == 0) {
+ // The color is completely transparent.
+ // For purposes of palettes and grayscale optimization,
+ // treat all channels as 0x00.
+ needsToZeroRGBChannelsOfTransparentPixels =
+ needsToZeroRGBChannelsOfTransparentPixels ||
+ (red != 0 || green != 0 || blue != 0);
+ red = green = blue = 0;
+ }
+
+ // Insert the color into the color palette.
+ const uint32_t color = red << 24 | green << 16 | blue << 8 | alpha;
+ colorPalette[color] = -1;
+
+ // If the pixel has non-opaque alpha, insert it into the
+ // alpha palette.
+ if (alpha != 0xff) {
+ alphaPalette.insert(color);
+ }
+
+ // Check if the image is indeed grayscale.
+ if (grayScale) {
+ if (red != green || red != blue) {
+ grayScale = false;
+ }
+ }
+
+ // Calculate the gray scale deviation so that it can be compared
+ // with the threshold.
+ maxGrayDeviation = std::max(std::abs(red - green), maxGrayDeviation);
+ maxGrayDeviation = std::max(std::abs(green - blue), maxGrayDeviation);
+ maxGrayDeviation = std::max(std::abs(blue - red), maxGrayDeviation);
+ }
+ }
+
+ if (context->verbose()) {
+ DiagMessage msg;
+ msg << " paletteSize=" << colorPalette.size()
+ << " alphaPaletteSize=" << alphaPalette.size()
+ << " maxGrayDeviation=" << maxGrayDeviation
+ << " grayScale=" << (grayScale ? "true" : "false");
+ context->getDiagnostics()->note(msg);
+ }
+
+ const bool convertibleToGrayScale = maxGrayDeviation <= options.grayScaleTolerance;
+
+ const int newColorType = pickColorType(image->width, image->height, grayScale,
+ convertibleToGrayScale, ninePatch != nullptr,
+ colorPalette.size(), alphaPalette.size());
+
+ if (context->verbose()) {
+ DiagMessage msg;
+ msg << "encoding PNG ";
+ if (ninePatch) {
+ msg << "(with 9-patch) as ";
+ }
+ switch (newColorType) {
+ case PNG_COLOR_TYPE_GRAY:
+ msg << "GRAY";
+ break;
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
+ msg << "GRAY + ALPHA";
+ break;
+ case PNG_COLOR_TYPE_RGB:
+ msg << "RGB";
+ break;
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ msg << "RGBA";
+ break;
+ case PNG_COLOR_TYPE_PALETTE:
+ msg << "PALETTE";
+ break;
+ default:
+ msg << "unknown type " << newColorType;
+ break;
+ }
+ context->getDiagnostics()->note(msg);
+ }
+
+ png_set_IHDR(writePtr, writeInfoPtr, image->width, image->height, 8, newColorType,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
+ if (newColorType & PNG_COLOR_MASK_PALETTE) {
+ // Assigns indices to the palette, and writes the encoded palette to the libpng writePtr.
+ writePalette(writePtr, writeInfoPtr, &colorPalette, &alphaPalette);
+ png_set_filter(writePtr, 0, PNG_NO_FILTERS);
+ } else {
+ png_set_filter(writePtr, 0, PNG_ALL_FILTERS);
+ }
+
+ if (ninePatch) {
+ writeNinePatch(writePtr, writeInfoPtr, ninePatch);
+ }
+
+ // Flush our updates to the header.
+ png_write_info(writePtr, writeInfoPtr);
+
+ // Write out each row of image data according to its encoding.
+ if (newColorType == PNG_COLOR_TYPE_PALETTE) {
+ // 1 byte/pixel.
+ auto outRow = std::unique_ptr<png_byte[]>(new png_byte[image->width]);
+
+ for (int32_t y = 0; y < image->height; y++) {
+ png_const_bytep inRow = image->rows[y];
+ for (int32_t x = 0; x < image->width; x++) {
+ int rr = *inRow++;
+ int gg = *inRow++;
+ int bb = *inRow++;
+ int aa = *inRow++;
+ if (aa == 0) {
+ // Zero out color channels when transparent.
+ rr = gg = bb = 0;
+ }
+
+ const uint32_t color = rr << 24 | gg << 16 | bb << 8 | aa;
+ const int idx = colorPalette[color];
+ assert(idx != -1);
+ outRow[x] = static_cast<png_byte>(idx);
+ }
+ png_write_row(writePtr, outRow.get());
+ }
+ } else if (newColorType == PNG_COLOR_TYPE_GRAY || newColorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
+ const size_t bpp = newColorType == PNG_COLOR_TYPE_GRAY ? 1 : 2;
+ auto outRow = std::unique_ptr<png_byte[]>(new png_byte[image->width * bpp]);
+
+ for (int32_t y = 0; y < image->height; y++) {
+ png_const_bytep inRow = image->rows[y];
+ for (int32_t x = 0; x < image->width; x++) {
+ int rr = inRow[x * 4];
+ int gg = inRow[x * 4 + 1];
+ int bb = inRow[x * 4 + 2];
+ int aa = inRow[x * 4 + 3];
+ if (aa == 0) {
+ // Zero out the gray channel when transparent.
+ rr = gg = bb = 0;
+ }
+
+ if (grayScale) {
+ // The image was already grayscale, red == green == blue.
+ outRow[x * bpp] = inRow[x * 4];
+ } else {
+ // The image is convertible to grayscale, use linear-luminance of
+ // sRGB colorspace: https://en.wikipedia.org/wiki/Grayscale#Colorimetric_.28luminance-preserving.29_conversion_to_grayscale
+ outRow[x * bpp] = (png_byte) (rr * 0.2126f + gg * 0.7152f + bb * 0.0722f);
+ }
+
+ if (bpp == 2) {
+ // Write out alpha if we have it.
+ outRow[x * bpp + 1] = aa;
+ }
+ }
+ png_write_row(writePtr, outRow.get());
+ }
+ } else if (newColorType == PNG_COLOR_TYPE_RGB || newColorType == PNG_COLOR_TYPE_RGBA) {
+ const size_t bpp = newColorType == PNG_COLOR_TYPE_RGB ? 3 : 4;
+ if (needsToZeroRGBChannelsOfTransparentPixels) {
+ // The source RGBA data can't be used as-is, because we need to zero out the RGB
+ // values of transparent pixels.
+ auto outRow = std::unique_ptr<png_byte[]>(new png_byte[image->width * bpp]);
+
+ for (int32_t y = 0; y < image->height; y++) {
+ png_const_bytep inRow = image->rows[y];
+ for (int32_t x = 0; x < image->width; x++) {
+ int rr = *inRow++;
+ int gg = *inRow++;
+ int bb = *inRow++;
+ int aa = *inRow++;
+ if (aa == 0) {
+ // Zero out the RGB channels when transparent.
+ rr = gg = bb = 0;
+ }
+ outRow[x * bpp] = rr;
+ outRow[x * bpp + 1] = gg;
+ outRow[x * bpp + 2] = bb;
+ if (bpp == 4) {
+ outRow[x * bpp + 3] = aa;
+ }
+ }
+ png_write_row(writePtr, outRow.get());
+ }
+ } else {
+ // The source image can be used as-is, just tell libpng whether or not to ignore
+ // the alpha channel.
+ if (newColorType == PNG_COLOR_TYPE_RGB) {
+ // Delete the extraneous alpha values that we appended to our buffer
+ // when reading the original values.
+ png_set_filler(writePtr, 0, PNG_FILLER_AFTER);
+ }
+ png_write_image(writePtr, image->rows.get());
+ }
+ } else {
+ assert(false && "unreachable");
+ }
+
+ png_write_end(writePtr, writeInfoPtr);
+ return true;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/cheap_transparency.png b/tools/aapt2/integration-tests/AppOne/res/drawable/cheap_transparency.png
new file mode 100644
index 0000000..0522a99
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/cheap_transparency.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/complex.9.png b/tools/aapt2/integration-tests/AppOne/res/drawable/complex.9.png
new file mode 100644
index 0000000..baf9fff
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/complex.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/outline_8x8.9.png b/tools/aapt2/integration-tests/AppOne/res/drawable/outline_8x8.9.png
new file mode 100644
index 0000000..7b331e1
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/outline_8x8.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_off_center_outline_32x16.9.png b/tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_off_center_outline_32x16.9.png
new file mode 100644
index 0000000..0ec6c70
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_off_center_outline_32x16.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_outline_32x16.9.png b/tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_outline_32x16.9.png
new file mode 100644
index 0000000..e05708a
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_outline_32x16.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/transparent_3x3.9.png b/tools/aapt2/integration-tests/AppOne/res/drawable/transparent_3x3.9.png
new file mode 100644
index 0000000..a11377a
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/transparent_3x3.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/transparent_optical_bounds_3x3.9.png b/tools/aapt2/integration-tests/AppOne/res/drawable/transparent_optical_bounds_3x3.9.png
new file mode 100644
index 0000000..6803e42
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/transparent_optical_bounds_3x3.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/white_3x3.9.png b/tools/aapt2/integration-tests/AppOne/res/drawable/white_3x3.9.png
new file mode 100644
index 0000000..1a3731b
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/white_3x3.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/white_optical_bounds_3x3.9.png b/tools/aapt2/integration-tests/AppOne/res/drawable/white_optical_bounds_3x3.9.png
new file mode 100644
index 0000000..489ace2
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/white_optical_bounds_3x3.9.png
Binary files differ
diff --git a/tools/aapt2/io/Io.cpp b/tools/aapt2/io/Io.cpp
new file mode 100644
index 0000000..963c21c
--- /dev/null
+++ b/tools/aapt2/io/Io.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "io/Io.h"
+
+#include <algorithm>
+#include <cstring>
+
+namespace aapt {
+namespace io {
+
+bool copy(OutputStream* out, InputStream* in) {
+ const void* inBuffer;
+ int inLen;
+ while (in->Next(&inBuffer, &inLen)) {
+ void* outBuffer;
+ int outLen;
+ if (!out->Next(&outBuffer, &outLen)) {
+ return !out->HadError();
+ }
+
+ const int bytesToCopy = std::min(inLen, outLen);
+ memcpy(outBuffer, inBuffer, bytesToCopy);
+ out->BackUp(outLen - bytesToCopy);
+ in->BackUp(inLen - bytesToCopy);
+ }
+ return !in->HadError();
+}
+
+} // namespace io
+} // namespace aapt
diff --git a/tools/aapt2/io/Io.h b/tools/aapt2/io/Io.h
new file mode 100644
index 0000000..e1e9107
--- /dev/null
+++ b/tools/aapt2/io/Io.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef AAPT_IO_IO_H
+#define AAPT_IO_IO_H
+
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+#include <string>
+
+namespace aapt {
+namespace io {
+
+/**
+ * InputStream interface that inherits from protobuf's ZeroCopyInputStream,
+ * but adds error handling methods to better report issues.
+ *
+ * The code style here matches the protobuf style.
+ */
+class InputStream : public google::protobuf::io::ZeroCopyInputStream {
+public:
+ virtual std::string GetError() const {
+ return {};
+ }
+
+ virtual bool HadError() const = 0;
+};
+
+/**
+ * OutputStream interface that inherits from protobuf's ZeroCopyOutputStream,
+ * but adds error handling methods to better report issues.
+ *
+ * The code style here matches the protobuf style.
+ */
+class OutputStream : public google::protobuf::io::ZeroCopyOutputStream {
+public:
+ virtual std::string GetError() const {
+ return {};
+ }
+
+ virtual bool HadError() const = 0;
+};
+
+/**
+ * Copies the data from in to out. Returns true if there was no error.
+ * If there was an error, check the individual streams' HadError/GetError
+ * methods.
+ */
+bool copy(OutputStream* out, InputStream* in);
+
+} // namespace io
+} // namespace aapt
+
+#endif /* AAPT_IO_IO_H */
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index c236394..b6b4b473 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -72,6 +72,7 @@
bool noAutoVersion = false;
bool noVersionVectors = false;
+ bool noResourceDeduping = false;
bool staticLib = false;
bool noStaticLibPackages = false;
bool generateNonFinalIds = false;
@@ -1505,6 +1506,14 @@
}
}
+ if (!mOptions.noResourceDeduping) {
+ ResourceDeduper deduper;
+ if (!deduper.consume(mContext, &mFinalTable)) {
+ mContext->getDiagnostics()->error(DiagMessage() << "failed deduping resources");
+ return 1;
+ }
+ }
+
proguard::KeepSet proguardKeepSet;
proguard::KeepSet proguardMainDexKeepSet;
@@ -1743,6 +1752,9 @@
"Disables automatic versioning of vector drawables. Use this only\n"
"when building with vector drawable support library",
&options.noVersionVectors)
+ .optionalSwitch("--no-resource-deduping", "Disables automatic deduping of resources with\n"
+ "identical values across compatible configurations.",
+ &options.noResourceDeduping)
.optionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01",
&legacyXFlag)
.optionalSwitch("-z", "Require localization of strings marked 'suggested'",
diff --git a/tools/aapt2/link/Linkers.h b/tools/aapt2/link/Linkers.h
index 82e2868..ce455da 100644
--- a/tools/aapt2/link/Linkers.h
+++ b/tools/aapt2/link/Linkers.h
@@ -60,6 +60,14 @@
};
/**
+ * Removes duplicated key-value entries from dominated resources.
+ */
+class ResourceDeduper : public IResourceTableConsumer {
+public:
+ bool consume(IAaptContext* context, ResourceTable* table) override;
+};
+
+/**
* If any attribute resource values are defined as public, this consumer will move all private
* attribute resource values to a private ^private-attr type, avoiding backwards compatibility
* issues with new apps running on old platforms.
diff --git a/tools/aapt2/link/ResourceDeduper.cpp b/tools/aapt2/link/ResourceDeduper.cpp
new file mode 100644
index 0000000..0276261
--- /dev/null
+++ b/tools/aapt2/link/ResourceDeduper.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "DominatorTree.h"
+#include "ResourceTable.h"
+#include "link/Linkers.h"
+
+#include <algorithm>
+
+namespace aapt {
+
+namespace {
+
+/**
+ * Remove duplicated key-value entries from dominated resources.
+ *
+ * Based on the dominator tree, we can remove a value of an entry if:
+ *
+ * 1. The configuration for the entry's value is dominated by a configuration
+ * with an equivalent entry value.
+ * 2. All compatible configurations for the entry (those not in conflict and
+ * unrelated by domination with the configuration for the entry's value) have
+ * an equivalent entry value.
+ */
+class DominatedKeyValueRemover : public DominatorTree::BottomUpVisitor {
+public:
+ using Node = DominatorTree::Node;
+
+ explicit DominatedKeyValueRemover(IAaptContext* context, ResourceEntry* entry) :
+ mContext(context), mEntry(entry) {
+ }
+
+ void visitConfig(Node* node) {
+ Node* parent = node->parent();
+ if (!parent) {
+ return;
+ }
+ ResourceConfigValue* nodeValue = node->value();
+ ResourceConfigValue* parentValue = parent->value();
+ if (!nodeValue || !parentValue) {
+ return;
+ }
+ if (!nodeValue->value->equals(parentValue->value.get())) {
+ return;
+ }
+
+ // Compare compatible configs for this entry and ensure the values are
+ // equivalent.
+ const ConfigDescription& nodeConfiguration = nodeValue->config;
+ for (const auto& sibling : mEntry->values) {
+ if (!sibling->value) {
+ // Sibling was already removed.
+ continue;
+ }
+ if (nodeConfiguration.isCompatibleWith(sibling->config)
+ && !nodeValue->value->equals(sibling->value.get())) {
+ // The configurations are compatible, but the value is
+ // different, so we can't remove this value.
+ return;
+ }
+ }
+ if (mContext->verbose()) {
+ mContext->getDiagnostics()->note(
+ DiagMessage(nodeValue->value->getSource())
+ << "removing dominated duplicate resource with name \""
+ << mEntry->name << "\"");
+ }
+ nodeValue->value = {};
+ }
+
+private:
+ IAaptContext* mContext;
+ ResourceEntry* mEntry;
+};
+
+static void dedupeEntry(IAaptContext* context, ResourceEntry* entry) {
+ DominatorTree tree(entry->values);
+ DominatedKeyValueRemover remover(context, entry);
+ tree.accept(&remover);
+
+ // Erase the values that were removed.
+ entry->values.erase(std::remove_if(entry->values.begin(), entry->values.end(),
+ [](const std::unique_ptr<ResourceConfigValue>& val) -> bool {
+ return val == nullptr || val->value == nullptr;
+ }), entry->values.end());
+}
+
+} // namespace
+
+bool ResourceDeduper::consume(IAaptContext* context, ResourceTable* table) {
+ for (auto& package : table->packages) {
+ for (auto& type : package->types) {
+ for (auto& entry : type->entries) {
+ dedupeEntry(context, entry.get());
+ }
+ }
+ }
+ return true;
+}
+
+} // aapt
diff --git a/tools/aapt2/link/ResourceDeduper_test.cpp b/tools/aapt2/link/ResourceDeduper_test.cpp
new file mode 100644
index 0000000..47071a51
--- /dev/null
+++ b/tools/aapt2/link/ResourceDeduper_test.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "ResourceTable.h"
+#include "link/Linkers.h"
+#include "test/Test.h"
+
+namespace aapt {
+
+TEST(ResourceDeduperTest, SameValuesAreDeduped) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ const ConfigDescription defaultConfig = {};
+ const ConfigDescription enConfig = test::parseConfigOrDie("en");
+ const ConfigDescription enV21Config = test::parseConfigOrDie("en-v21");
+ // Chosen because this configuration is compatible with en.
+ const ConfigDescription landConfig = test::parseConfigOrDie("land");
+
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .addString("android:string/dedupe", ResourceId{}, defaultConfig, "dedupe")
+ .addString("android:string/dedupe", ResourceId{}, enConfig, "dedupe")
+ .addString("android:string/dedupe", ResourceId{}, landConfig, "dedupe")
+ .addString("android:string/dedupe2", ResourceId{}, defaultConfig, "dedupe")
+ .addString("android:string/dedupe2", ResourceId{}, enConfig, "dedupe")
+ .addString("android:string/dedupe2", ResourceId{}, enV21Config, "keep")
+ .addString("android:string/dedupe2", ResourceId{}, landConfig, "dedupe")
+ .build();
+
+ ASSERT_TRUE(ResourceDeduper().consume(context.get(), table.get()));
+ EXPECT_EQ(
+ nullptr,
+ test::getValueForConfig<String>(table.get(), "android:string/dedupe", enConfig));
+ EXPECT_EQ(
+ nullptr,
+ test::getValueForConfig<String>(table.get(), "android:string/dedupe", landConfig));
+ EXPECT_EQ(
+ nullptr,
+ test::getValueForConfig<String>(table.get(), "android:string/dedupe2", enConfig));
+ EXPECT_NE(
+ nullptr,
+ test::getValueForConfig<String>(table.get(), "android:string/dedupe2", enV21Config));
+}
+
+TEST(ResourceDeduperTest, DifferentValuesAreKept) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ const ConfigDescription defaultConfig = {};
+ const ConfigDescription enConfig = test::parseConfigOrDie("en");
+ const ConfigDescription enV21Config = test::parseConfigOrDie("en-v21");
+ // Chosen because this configuration is compatible with en.
+ const ConfigDescription landConfig = test::parseConfigOrDie("land");
+
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .addString("android:string/keep", ResourceId{}, defaultConfig, "keep")
+ .addString("android:string/keep", ResourceId{}, enConfig, "keep")
+ .addString("android:string/keep", ResourceId{}, enV21Config, "keep2")
+ .addString("android:string/keep", ResourceId{}, landConfig, "keep2")
+ .build();
+
+ ASSERT_TRUE(ResourceDeduper().consume(context.get(), table.get()));
+ EXPECT_NE(
+ nullptr,
+ test::getValueForConfig<String>(table.get(), "android:string/keep", enConfig));
+ EXPECT_NE(
+ nullptr,
+ test::getValueForConfig<String>(table.get(), "android:string/keep", enV21Config));
+ EXPECT_NE(
+ nullptr,
+ test::getValueForConfig<String>(table.get(), "android:string/keep", landConfig));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/readme.md b/tools/aapt2/readme.md
index 267200d..93c790d 100644
--- a/tools/aapt2/readme.md
+++ b/tools/aapt2/readme.md
@@ -4,6 +4,10 @@
### `aapt2 compile ...`
- Added support for inline complex XML resources. See
https://developer.android.com/guide/topics/resources/complex-xml-resources.html
+### `aapt link ...`
+- Duplicate resource filtering: removes duplicate resources in dominated configurations
+ that are always identical when selected at runtime. This can be disabled with
+ `--no-resource-deduping`.
## Version 2.1
### `aapt2 link ...`
diff --git a/tools/aapt2/util/BigBuffer.cpp b/tools/aapt2/util/BigBuffer.cpp
index c88e3c1..de4ecd2 100644
--- a/tools/aapt2/util/BigBuffer.cpp
+++ b/tools/aapt2/util/BigBuffer.cpp
@@ -49,4 +49,29 @@
return mBlocks.back().buffer.get();
}
+void* BigBuffer::nextBlock(size_t* outSize) {
+ if (!mBlocks.empty()) {
+ Block& block = mBlocks.back();
+ if (block.size != block.mBlockSize) {
+ void* outBuffer = block.buffer.get() + block.size;
+ size_t size = block.mBlockSize - block.size;
+ block.size = block.mBlockSize;
+ mSize += size;
+ *outSize = size;
+ return outBuffer;
+ }
+ }
+
+ // Zero-allocate the block's buffer.
+ Block block = {};
+ block.buffer = std::unique_ptr<uint8_t[]>(new uint8_t[mBlockSize]());
+ assert(block.buffer);
+ block.size = mBlockSize;
+ block.mBlockSize = mBlockSize;
+ mBlocks.push_back(std::move(block));
+ mSize += mBlockSize;
+ *outSize = mBlockSize;
+ return mBlocks.back().buffer.get();
+}
+
} // namespace aapt
diff --git a/tools/aapt2/util/BigBuffer.h b/tools/aapt2/util/BigBuffer.h
index ba8532f..685614f 100644
--- a/tools/aapt2/util/BigBuffer.h
+++ b/tools/aapt2/util/BigBuffer.h
@@ -82,6 +82,20 @@
T* nextBlock(size_t count = 1);
/**
+ * Returns the next block available and puts the size in outCount.
+ * This is useful for grabbing blocks where the size doesn't matter.
+ * Use backUp() to give back any bytes that were not used.
+ */
+ void* nextBlock(size_t* outCount);
+
+ /**
+ * Backs up count bytes. This must only be called after nextBlock()
+ * and can not be larger than sizeof(T) * count of the last nextBlock()
+ * call.
+ */
+ void backUp(size_t count);
+
+ /**
* Moves the specified BigBuffer into this one. When this method
* returns, buffer is empty.
*/
@@ -97,6 +111,8 @@
*/
void align4();
+ size_t getBlockSize() const;
+
const_iterator begin() const;
const_iterator end() const;
@@ -123,6 +139,10 @@
return mSize;
}
+inline size_t BigBuffer::getBlockSize() const {
+ return mBlockSize;
+}
+
template <typename T>
inline T* BigBuffer::nextBlock(size_t count) {
static_assert(std::is_standard_layout<T>::value, "T must be standard_layout type");
@@ -130,6 +150,12 @@
return reinterpret_cast<T*>(nextBlockImpl(sizeof(T) * count));
}
+inline void BigBuffer::backUp(size_t count) {
+ Block& block = mBlocks.back();
+ block.size -= count;
+ mSize -= count;
+}
+
inline void BigBuffer::appendBuffer(BigBuffer&& buffer) {
std::move(buffer.mBlocks.begin(), buffer.mBlocks.end(), std::back_inserter(mBlocks));
mSize += buffer.mSize;
diff --git a/tools/aapt2/util/StringPiece.h b/tools/aapt2/util/StringPiece.h
index 4300a67..266c003 100644
--- a/tools/aapt2/util/StringPiece.h
+++ b/tools/aapt2/util/StringPiece.h
@@ -39,6 +39,9 @@
using const_iterator = const TChar*;
using difference_type = size_t;
+ // End of string marker.
+ constexpr static const size_t npos = static_cast<size_t>(-1);
+
BasicStringPiece();
BasicStringPiece(const BasicStringPiece<TChar>& str);
BasicStringPiece(const std::basic_string<TChar>& str); // NOLINT(implicit)
@@ -48,7 +51,7 @@
BasicStringPiece<TChar>& operator=(const BasicStringPiece<TChar>& rhs);
BasicStringPiece<TChar>& assign(const TChar* str, size_t len);
- BasicStringPiece<TChar> substr(size_t start, size_t len) const;
+ BasicStringPiece<TChar> substr(size_t start, size_t len = npos) const;
BasicStringPiece<TChar> substr(BasicStringPiece<TChar>::const_iterator begin,
BasicStringPiece<TChar>::const_iterator end) const;
@@ -81,6 +84,9 @@
//
template <typename TChar>
+constexpr const size_t BasicStringPiece<TChar>::npos;
+
+template <typename TChar>
inline BasicStringPiece<TChar>::BasicStringPiece() : mData(nullptr) , mLength(0) {
}
@@ -127,7 +133,11 @@
template <typename TChar>
inline BasicStringPiece<TChar> BasicStringPiece<TChar>::substr(size_t start, size_t len) const {
- if (start + len > mLength) {
+ if (len == npos) {
+ len = mLength - start;
+ }
+
+ if (start > mLength || start + len > mLength) {
return BasicStringPiece<TChar>();
}
return BasicStringPiece<TChar>(mData + start, len);
diff --git a/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl b/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
index 56baba9..5485824 100644
--- a/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
+++ b/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
@@ -23,6 +23,7 @@
import android.net.wifi.nan.IWifiNanEventCallback;
import android.net.wifi.nan.PublishConfig;
import android.net.wifi.nan.SubscribeConfig;
+import android.net.wifi.nan.WifiNanCharacteristics;
import android.net.wifi.RttManager;
/**
@@ -36,6 +37,7 @@
void enableUsage();
void disableUsage();
boolean isUsageEnabled();
+ WifiNanCharacteristics getCharacteristics();
// client API
void connect(in IBinder binder, in String callingPackage, in IWifiNanEventCallback callback,
diff --git a/wifi/java/android/net/wifi/nan/PublishConfig.java b/wifi/java/android/net/wifi/nan/PublishConfig.java
index 4b67f9a..30c5bc0 100644
--- a/wifi/java/android/net/wifi/nan/PublishConfig.java
+++ b/wifi/java/android/net/wifi/nan/PublishConfig.java
@@ -182,7 +182,7 @@
*
* @hide
*/
- public void validate() throws IllegalArgumentException {
+ public void assertValid(WifiNanCharacteristics characteristics) throws IllegalArgumentException {
WifiNanUtils.validateServiceName(mServiceName);
if (!LvBufferUtils.isValid(mMatchFilter, 1)) {
@@ -198,6 +198,26 @@
if (mTtlSec < 0) {
throw new IllegalArgumentException("Invalid ttlSec - must be non-negative");
}
+
+ if (characteristics != null) {
+ int maxServiceNameLength = characteristics.getMaxServiceNameLength();
+ if (maxServiceNameLength != 0 && mServiceName.length > maxServiceNameLength) {
+ throw new IllegalArgumentException(
+ "Service name longer than supported by device characteristics");
+ }
+ int maxServiceSpecificInfoLength = characteristics.getMaxServiceSpecificInfoLength();
+ if (maxServiceSpecificInfoLength != 0 && mServiceSpecificInfo != null
+ && mServiceSpecificInfo.length > maxServiceSpecificInfoLength) {
+ throw new IllegalArgumentException(
+ "Service specific info longer than supported by device characteristics");
+ }
+ int maxMatchFilterLength = characteristics.getMaxMatchFilterLength();
+ if (maxMatchFilterLength != 0 && mMatchFilter != null
+ && mMatchFilter.length > maxMatchFilterLength) {
+ throw new IllegalArgumentException(
+ "Match filter longer than supported by device characteristics");
+ }
+ }
}
/**
diff --git a/wifi/java/android/net/wifi/nan/SubscribeConfig.java b/wifi/java/android/net/wifi/nan/SubscribeConfig.java
index 4352fcf..ea7b8e4 100644
--- a/wifi/java/android/net/wifi/nan/SubscribeConfig.java
+++ b/wifi/java/android/net/wifi/nan/SubscribeConfig.java
@@ -209,7 +209,7 @@
*
* @hide
*/
- public void validate() throws IllegalArgumentException {
+ public void assertValid(WifiNanCharacteristics characteristics) throws IllegalArgumentException {
WifiNanUtils.validateServiceName(mServiceName);
if (!LvBufferUtils.isValid(mMatchFilter, 1)) {
@@ -229,6 +229,26 @@
throw new IllegalArgumentException(
"Invalid matchType - must be MATCH_FIRST_ONLY or MATCH_ALL");
}
+
+ if (characteristics != null) {
+ int maxServiceNameLength = characteristics.getMaxServiceNameLength();
+ if (maxServiceNameLength != 0 && mServiceName.length > maxServiceNameLength) {
+ throw new IllegalArgumentException(
+ "Service name longer than supported by device characteristics");
+ }
+ int maxServiceSpecificInfoLength = characteristics.getMaxServiceSpecificInfoLength();
+ if (maxServiceSpecificInfoLength != 0 && mServiceSpecificInfo != null
+ && mServiceSpecificInfo.length > maxServiceSpecificInfoLength) {
+ throw new IllegalArgumentException(
+ "Service specific info longer than supported by device characteristics");
+ }
+ int maxMatchFilterLength = characteristics.getMaxMatchFilterLength();
+ if (maxMatchFilterLength != 0 && mMatchFilter != null
+ && mMatchFilter.length > maxMatchFilterLength) {
+ throw new IllegalArgumentException(
+ "Match filter longer than supported by device characteristics");
+ }
+ }
}
/**
diff --git a/core/tests/coretests/src/android/print/mockservice/SettingsActivity.java b/wifi/java/android/net/wifi/nan/WifiNanCharacteristics.aidl
similarity index 69%
rename from core/tests/coretests/src/android/print/mockservice/SettingsActivity.java
rename to wifi/java/android/net/wifi/nan/WifiNanCharacteristics.aidl
index fb76e67..e562a00 100644
--- a/core/tests/coretests/src/android/print/mockservice/SettingsActivity.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanCharacteristics.aidl
@@ -14,15 +14,6 @@
* limitations under the License.
*/
-package android.print.mockservice;
+package android.net.wifi.nan;
-import android.app.Activity;
-import android.os.Bundle;
-
-public class SettingsActivity extends Activity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
-}
+parcelable WifiNanCharacteristics;
diff --git a/wifi/java/android/net/wifi/nan/WifiNanCharacteristics.java b/wifi/java/android/net/wifi/nan/WifiNanCharacteristics.java
new file mode 100644
index 0000000..f43ed4d
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/WifiNanCharacteristics.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.nan;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * The characteristics of the Wi-Fi NAN implementation.
+ *
+ * @hide PROPOSED_NAN_API
+ */
+public class WifiNanCharacteristics implements Parcelable {
+ /** @hide */
+ public static final String KEY_MAX_SERVICE_NAME_LENGTH = "key_max_service_name_length";
+ /** @hide */
+ public static final String KEY_MAX_SERVICE_SPECIFIC_INFO_LENGTH =
+ "key_max_service_specific_info_length";
+ /** @hide */
+ public static final String KEY_MAX_MATCH_FILTER_LENGTH = "key_max_match_filter_length";
+
+ private Bundle mCharacteristics = new Bundle();
+
+ /** @hide : should not be created by apps */
+ public WifiNanCharacteristics(Bundle characteristics) {
+ mCharacteristics = characteristics;
+ }
+
+ /**
+ * Returns the maximum string length that can be used to specify a NAN service name. Restricts
+ * the parameters of the {@link PublishConfig.Builder#setServiceName(String)} and
+ * {@link SubscribeConfig.Builder#setServiceName(String)}.
+ *
+ * @return A positive integer, maximum string length of NAN service name.
+ */
+ public int getMaxServiceNameLength() {
+ return mCharacteristics.getInt(KEY_MAX_SERVICE_NAME_LENGTH);
+ }
+
+ /**
+ * Returns the maximum length of byte array that can be used to specify a NAN service specific
+ * information field: the arbitrary load used in discovery or the message length of NAN
+ * message exchange. Restricts the parameters of the
+ * {@link PublishConfig.Builder#setServiceSpecificInfo(byte[])},
+ * {@link SubscribeConfig.Builder#setServiceSpecificInfo(byte[])}, and
+ * {@link WifiNanDiscoveryBaseSession#sendMessage(Object, int, byte[])} variants.
+ *
+ * @return A positive integer, maximum length of byte array for NAN messaging.
+ */
+ public int getMaxServiceSpecificInfoLength() {
+ return mCharacteristics.getInt(KEY_MAX_SERVICE_SPECIFIC_INFO_LENGTH);
+ }
+
+ /**
+ * Returns the maximum length of byte array that can be used to specify a NAN match filter.
+ * Restricts the parameters of the {@link PublishConfig.Builder#setMatchFilter(byte[])} and
+ * {@link SubscribeConfig.Builder#setMatchFilter(byte[])}.
+ *
+ * @return A positive integer, maximum legngth of byte array for NAN discovery match filter.
+ */
+ public int getMaxMatchFilterLength() {
+ return mCharacteristics.getInt(KEY_MAX_MATCH_FILTER_LENGTH);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeBundle(mCharacteristics);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<WifiNanCharacteristics> CREATOR =
+ new Creator<WifiNanCharacteristics>() {
+ @Override
+ public WifiNanCharacteristics createFromParcel(Parcel in) {
+ WifiNanCharacteristics c = new WifiNanCharacteristics(in.readBundle());
+ return c;
+ }
+
+ @Override
+ public WifiNanCharacteristics[] newArray(int size) {
+ return new WifiNanCharacteristics[size];
+ }
+ };
+}
diff --git a/wifi/java/android/net/wifi/nan/WifiNanManager.java b/wifi/java/android/net/wifi/nan/WifiNanManager.java
index bb15434..002b953 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanManager.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanManager.java
@@ -293,6 +293,20 @@
}
/**
+ * Returns the characteristics of the Wi-Fi NAN interface: a set of parameters which specify
+ * limitations on configurations, e.g. the maximum service name length.
+ *
+ * @return An object specifying configuration limitations of NAN.
+ */
+ public WifiNanCharacteristics getCharacteristics() {
+ try {
+ return mService.getCharacteristics();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Attach to the Wi-Fi NAN service - enabling the application to create discovery sessions or
* create connections to peers. The device will attach to an existing cluster if it can find
* one or create a new cluster (if it is the first to enable NAN in its vicinity). Results
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSession.java b/wifi/java/android/net/wifi/nan/WifiNanSession.java
index 5fb2c06..df5e3c1 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanSession.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanSession.java
@@ -113,6 +113,8 @@
* An application must use the {@link WifiNanDiscoveryBaseSession#destroy()} to
* terminate the publish discovery session once it isn't needed. This will free
* resources as well terminate any on-air transmissions.
+ * <p>The application must have the {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}
+ * permission to start a publish discovery session.
*
* @param handler The Handler on whose thread to execute the callbacks of the {@code
* callback} object. If a null is provided then the application's main thread will be used.
@@ -156,6 +158,8 @@
* An application must use the {@link WifiNanDiscoveryBaseSession#destroy()} to
* terminate the subscribe discovery session once it isn't needed. This will free
* resources as well terminate any on-air transmissions.
+ * <p>The application must have the {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}
+ * permission to start a subscribe discovery session.
*
* @param handler The Handler on whose thread to execute the callbacks of the {@code
* callback} object. If a null is provided then the application's main thread will be used.