summaryrefslogtreecommitdiff
path: root/graphics
diff options
context:
space:
mode:
Diffstat (limited to 'graphics')
-rw-r--r--graphics/java/android/graphics/FontFamily.java20
-rw-r--r--graphics/java/android/graphics/GraphicsStatsService.java566
-rw-r--r--graphics/java/android/graphics/HardwareRenderer.java5
-rw-r--r--graphics/java/android/graphics/fonts/Font.java104
4 files changed, 644 insertions, 51 deletions
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index 447f043392c2..f50de1665453 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -19,6 +19,7 @@ package android.graphics;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.AssetManager;
+import android.graphics.fonts.Font;
import android.graphics.fonts.FontVariationAxis;
import android.os.Build;
import android.text.TextUtils;
@@ -195,18 +196,13 @@ public class FontFamily {
if (mBuilderPtr == 0) {
throw new IllegalStateException("Unable to call addFontFromAsset after freezing.");
}
- if (axes != null) {
- for (FontVariationAxis axis : axes) {
- nAddAxisValue(mBuilderPtr, axis.getOpenTypeTagValue(), axis.getStyleValue());
- }
- }
- return nAddFontFromAssetManager(mBuilderPtr, mgr, path, cookie, isAsset, ttcIndex, weight,
- isItalic);
- }
- // TODO: Remove once internal user stop using private API.
- private static boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex) {
- return nAddFont(builderPtr, font, ttcIndex, -1, -1);
+ try {
+ ByteBuffer buffer = Font.Builder.createBuffer(mgr, path, isAsset, cookie);
+ return addFontFromBuffer(buffer, ttcIndex, axes, weight, isItalic);
+ } catch (IOException e) {
+ return false;
+ }
}
private static native long nInitBuilder(String langs, int variant);
@@ -225,8 +221,6 @@ public class FontFamily {
int weight, int isItalic);
private static native boolean nAddFontWeightStyle(long builderPtr, ByteBuffer font,
int ttcIndex, int weight, int isItalic);
- private static native boolean nAddFontFromAssetManager(long builderPtr, AssetManager mgr,
- String path, int cookie, boolean isAsset, int ttcIndex, int weight, int isItalic);
// The added axis values are only valid for the next nAddFont* method call.
@CriticalNative
diff --git a/graphics/java/android/graphics/GraphicsStatsService.java b/graphics/java/android/graphics/GraphicsStatsService.java
new file mode 100644
index 000000000000..8dfd6ee92a9a
--- /dev/null
+++ b/graphics/java/android/graphics/GraphicsStatsService.java
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2015 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;
+
+import android.annotation.SystemApi;
+import android.app.AlarmManager;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SharedMemory;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.system.ErrnoException;
+import android.util.Log;
+import android.view.IGraphicsStats;
+import android.view.IGraphicsStatsCallback;
+
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FastPrintWriter;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.HashSet;
+import java.util.TimeZone;
+
+/**
+ * This service's job is to collect aggregate rendering profile data. It
+ * does this by allowing rendering processes to request an ashmem buffer
+ * to place their stats into.
+ *
+ * Buffers are rotated on a daily (in UTC) basis and only the 3 most-recent days
+ * are kept.
+ *
+ * The primary consumer of this is incident reports and automated metric checking. It is not
+ * intended for end-developer consumption, for that we have gfxinfo.
+ *
+ * Buffer rotation process:
+ * 1) Alarm fires
+ * 2) onRotateGraphicsStatsBuffer() is sent to all active processes
+ * 3) Upon receiving the callback, the process will stop using the previous ashmem buffer and
+ * request a new one.
+ * 4) When that request is received we now know that the ashmem region is no longer in use so
+ * it gets queued up for saving to disk and a new ashmem region is created and returned
+ * for the process to use.
+ *
+ * @hide */
+public class GraphicsStatsService extends IGraphicsStats.Stub {
+ public static final String GRAPHICS_STATS_SERVICE = "graphicsstats";
+
+ private static final String TAG = "GraphicsStatsService";
+
+ private static final int SAVE_BUFFER = 1;
+ private static final int DELETE_OLD = 2;
+
+ private static final int AID_STATSD = 1066; // Statsd uid is set to 1066 forever.
+
+ // This isn't static because we need this to happen after registerNativeMethods, however
+ // the class is loaded (and thus static ctor happens) before that occurs.
+ private final int mAshmemSize = nGetAshmemSize();
+ private final byte[] mZeroData = new byte[mAshmemSize];
+
+ private final Context mContext;
+ private final AppOpsManager mAppOps;
+ private final AlarmManager mAlarmManager;
+ private final Object mLock = new Object();
+ private ArrayList<ActiveBuffer> mActive = new ArrayList<>();
+ private File mGraphicsStatsDir;
+ private final Object mFileAccessLock = new Object();
+ private Handler mWriteOutHandler;
+ private boolean mRotateIsScheduled = false;
+
+ @SystemApi
+ public GraphicsStatsService(Context context) {
+ mContext = context;
+ mAppOps = context.getSystemService(AppOpsManager.class);
+ mAlarmManager = context.getSystemService(AlarmManager.class);
+ File systemDataDir = new File(Environment.getDataDirectory(), "system");
+ mGraphicsStatsDir = new File(systemDataDir, "graphicsstats");
+ mGraphicsStatsDir.mkdirs();
+ if (!mGraphicsStatsDir.exists()) {
+ throw new IllegalStateException("Graphics stats directory does not exist: "
+ + mGraphicsStatsDir.getAbsolutePath());
+ }
+ HandlerThread bgthread = new HandlerThread("GraphicsStats-disk",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ bgthread.start();
+
+ mWriteOutHandler = new Handler(bgthread.getLooper(), new Handler.Callback() {
+ @Override
+ public boolean handleMessage(Message msg) {
+ switch (msg.what) {
+ case SAVE_BUFFER:
+ saveBuffer((HistoricalBuffer) msg.obj);
+ break;
+ case DELETE_OLD:
+ deleteOldBuffers();
+ break;
+ }
+ return true;
+ }
+ });
+ nativeInit();
+ }
+
+ /**
+ * Current rotation policy is to rotate at midnight UTC. We don't specify RTC_WAKEUP because
+ * rotation can be delayed if there's otherwise no activity. However exact is used because
+ * we don't want the system to delay it by TOO much.
+ */
+ private void scheduleRotateLocked() {
+ if (mRotateIsScheduled) {
+ return;
+ }
+ mRotateIsScheduled = true;
+ Calendar calendar = normalizeDate(System.currentTimeMillis());
+ calendar.add(Calendar.DATE, 1);
+ mAlarmManager.setExact(AlarmManager.RTC, calendar.getTimeInMillis(), TAG, this::onAlarm,
+ mWriteOutHandler);
+ }
+
+ private void onAlarm() {
+ // We need to make a copy since some of the callbacks won't be proxy and thus
+ // can result in a re-entrant acquisition of mLock that would result in a modification
+ // of mActive during iteration.
+ ActiveBuffer[] activeCopy;
+ synchronized (mLock) {
+ mRotateIsScheduled = false;
+ scheduleRotateLocked();
+ activeCopy = mActive.toArray(new ActiveBuffer[0]);
+ }
+ for (ActiveBuffer active : activeCopy) {
+ try {
+ active.mCallback.onRotateGraphicsStatsBuffer();
+ } catch (RemoteException e) {
+ Log.w(TAG, String.format("Failed to notify '%s' (pid=%d) to rotate buffers",
+ active.mInfo.mPackageName, active.mPid), e);
+ }
+ }
+ // Give a few seconds for everyone to rotate before doing the cleanup
+ mWriteOutHandler.sendEmptyMessageDelayed(DELETE_OLD, 10000);
+ }
+
+ @Override
+ public ParcelFileDescriptor requestBufferForProcess(String packageName,
+ IGraphicsStatsCallback token) throws RemoteException {
+ int uid = Binder.getCallingUid();
+ int pid = Binder.getCallingPid();
+ ParcelFileDescriptor pfd = null;
+ long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ mAppOps.checkPackage(uid, packageName);
+ PackageInfo info = mContext.getPackageManager().getPackageInfoAsUser(
+ packageName,
+ 0,
+ UserHandle.getUserId(uid));
+ synchronized (mLock) {
+ pfd = requestBufferForProcessLocked(token, uid, pid, packageName,
+ info.getLongVersionCode());
+ }
+ } catch (PackageManager.NameNotFoundException ex) {
+ throw new RemoteException("Unable to find package: '" + packageName + "'");
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ return pfd;
+ }
+
+ // If lastFullDay is true, pullGraphicsStats returns stats for the last complete day/24h period
+ // that does not include today. If lastFullDay is false, pullGraphicsStats returns stats for the
+ // current day.
+ // This method is invoked from native code only.
+ @SuppressWarnings({"UnusedDeclaration"})
+ private void pullGraphicsStats(boolean lastFullDay, long pulledData) throws RemoteException {
+ int uid = Binder.getCallingUid();
+
+ // DUMP and PACKAGE_USAGE_STATS permissions are required to invoke this method.
+ // TODO: remove exception for statsd daemon after required permissions are granted. statsd
+ // TODO: should have these permissions granted by data/etc/platform.xml, but it does not.
+ if (uid != AID_STATSD) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new FastPrintWriter(sw);
+ if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) {
+ pw.flush();
+ throw new RemoteException(sw.toString());
+ }
+ }
+
+ long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ pullGraphicsStatsImpl(lastFullDay, pulledData);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ private void pullGraphicsStatsImpl(boolean lastFullDay, long pulledData) {
+ long targetDay;
+ if (lastFullDay) {
+ // Get stats from yesterday. Stats stay constant, because the day is over.
+ targetDay = normalizeDate(System.currentTimeMillis() - 86400000).getTimeInMillis();
+ } else {
+ // Get stats from today. Stats may change as more apps are run today.
+ targetDay = normalizeDate(System.currentTimeMillis()).getTimeInMillis();
+ }
+
+ // Find active buffers for targetDay.
+ ArrayList<HistoricalBuffer> buffers;
+ synchronized (mLock) {
+ buffers = new ArrayList<>(mActive.size());
+ for (int i = 0; i < mActive.size(); i++) {
+ ActiveBuffer buffer = mActive.get(i);
+ if (buffer.mInfo.mStartTime == targetDay) {
+ try {
+ buffers.add(new HistoricalBuffer(buffer));
+ } catch (IOException ex) {
+ // Ignore
+ }
+ }
+ }
+ }
+
+ // Dump active and historic buffers for targetDay in a serialized
+ // GraphicsStatsServiceDumpProto proto.
+ long dump = nCreateDump(-1, true);
+ try {
+ synchronized (mFileAccessLock) {
+ HashSet<File> skipList = dumpActiveLocked(dump, buffers);
+ buffers.clear();
+ String subPath = String.format("%d", targetDay);
+ File dateDir = new File(mGraphicsStatsDir, subPath);
+ if (dateDir.exists()) {
+ for (File pkg : dateDir.listFiles()) {
+ for (File version : pkg.listFiles()) {
+ File data = new File(version, "total");
+ if (skipList.contains(data)) {
+ continue;
+ }
+ nAddToDump(dump, data.getAbsolutePath());
+ }
+ }
+ }
+ }
+ } finally {
+ nFinishDumpInMemory(dump, pulledData, lastFullDay);
+ }
+ }
+
+ private ParcelFileDescriptor requestBufferForProcessLocked(IGraphicsStatsCallback token,
+ int uid, int pid, String packageName, long versionCode) throws RemoteException {
+ ActiveBuffer buffer = fetchActiveBuffersLocked(token, uid, pid, packageName, versionCode);
+ scheduleRotateLocked();
+ return buffer.getPfd();
+ }
+
+ private Calendar normalizeDate(long timestamp) {
+ Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ calendar.setTimeInMillis(timestamp);
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ calendar.set(Calendar.MILLISECOND, 0);
+ return calendar;
+ }
+
+ private File pathForApp(BufferInfo info) {
+ String subPath = String.format("%d/%s/%d/total",
+ normalizeDate(info.mStartTime).getTimeInMillis(), info.mPackageName,
+ info.mVersionCode);
+ return new File(mGraphicsStatsDir, subPath);
+ }
+
+ private void saveBuffer(HistoricalBuffer buffer) {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER,
+ "saving graphicsstats for " + buffer.mInfo.mPackageName);
+ }
+ synchronized (mFileAccessLock) {
+ File path = pathForApp(buffer.mInfo);
+ File parent = path.getParentFile();
+ parent.mkdirs();
+ if (!parent.exists()) {
+ Log.w(TAG, "Unable to create path: '" + parent.getAbsolutePath() + "'");
+ return;
+ }
+ nSaveBuffer(path.getAbsolutePath(), buffer.mInfo.mPackageName,
+ buffer.mInfo.mVersionCode, buffer.mInfo.mStartTime, buffer.mInfo.mEndTime,
+ buffer.mData);
+ }
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ }
+
+ private void deleteRecursiveLocked(File file) {
+ if (file.isDirectory()) {
+ for (File child : file.listFiles()) {
+ deleteRecursiveLocked(child);
+ }
+ }
+ if (!file.delete()) {
+ Log.w(TAG, "Failed to delete '" + file.getAbsolutePath() + "'!");
+ }
+ }
+
+ private void deleteOldBuffers() {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "deleting old graphicsstats buffers");
+ synchronized (mFileAccessLock) {
+ File[] files = mGraphicsStatsDir.listFiles();
+ if (files == null || files.length <= 3) {
+ return;
+ }
+ long[] sortedDates = new long[files.length];
+ for (int i = 0; i < files.length; i++) {
+ try {
+ sortedDates[i] = Long.parseLong(files[i].getName());
+ } catch (NumberFormatException ex) {
+ // Skip unrecognized folders
+ }
+ }
+ if (sortedDates.length <= 3) {
+ return;
+ }
+ Arrays.sort(sortedDates);
+ for (int i = 0; i < sortedDates.length - 3; i++) {
+ deleteRecursiveLocked(new File(mGraphicsStatsDir, Long.toString(sortedDates[i])));
+ }
+ }
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ }
+
+ private void addToSaveQueue(ActiveBuffer buffer) {
+ try {
+ HistoricalBuffer data = new HistoricalBuffer(buffer);
+ Message.obtain(mWriteOutHandler, SAVE_BUFFER, data).sendToTarget();
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to copy graphicsstats from " + buffer.mInfo.mPackageName, e);
+ }
+ buffer.closeAllBuffers();
+ }
+
+ private void processDied(ActiveBuffer buffer) {
+ synchronized (mLock) {
+ mActive.remove(buffer);
+ }
+ addToSaveQueue(buffer);
+ }
+
+ private ActiveBuffer fetchActiveBuffersLocked(IGraphicsStatsCallback token, int uid, int pid,
+ String packageName, long versionCode) throws RemoteException {
+ int size = mActive.size();
+ long today = normalizeDate(System.currentTimeMillis()).getTimeInMillis();
+ for (int i = 0; i < size; i++) {
+ ActiveBuffer buffer = mActive.get(i);
+ if (buffer.mPid == pid
+ && buffer.mUid == uid) {
+ // If the buffer is too old we remove it and return a new one
+ if (buffer.mInfo.mStartTime < today) {
+ buffer.binderDied();
+ break;
+ } else {
+ return buffer;
+ }
+ }
+ }
+ // Didn't find one, need to create it
+ try {
+ ActiveBuffer buffers = new ActiveBuffer(token, uid, pid, packageName, versionCode);
+ mActive.add(buffers);
+ return buffers;
+ } catch (IOException ex) {
+ throw new RemoteException("Failed to allocate space");
+ }
+ }
+
+ private HashSet<File> dumpActiveLocked(long dump, ArrayList<HistoricalBuffer> buffers) {
+ HashSet<File> skipFiles = new HashSet<>(buffers.size());
+ for (int i = 0; i < buffers.size(); i++) {
+ HistoricalBuffer buffer = buffers.get(i);
+ File path = pathForApp(buffer.mInfo);
+ skipFiles.add(path);
+ nAddToDump(dump, path.getAbsolutePath(), buffer.mInfo.mPackageName,
+ buffer.mInfo.mVersionCode, buffer.mInfo.mStartTime, buffer.mInfo.mEndTime,
+ buffer.mData);
+ }
+ return skipFiles;
+ }
+
+ private void dumpHistoricalLocked(long dump, HashSet<File> skipFiles) {
+ for (File date : mGraphicsStatsDir.listFiles()) {
+ for (File pkg : date.listFiles()) {
+ for (File version : pkg.listFiles()) {
+ File data = new File(version, "total");
+ if (skipFiles.contains(data)) {
+ continue;
+ }
+ nAddToDump(dump, data.getAbsolutePath());
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, fout)) return;
+ boolean dumpProto = false;
+ for (String str : args) {
+ if ("--proto".equals(str)) {
+ dumpProto = true;
+ break;
+ }
+ }
+ ArrayList<HistoricalBuffer> buffers;
+ synchronized (mLock) {
+ buffers = new ArrayList<>(mActive.size());
+ for (int i = 0; i < mActive.size(); i++) {
+ try {
+ buffers.add(new HistoricalBuffer(mActive.get(i)));
+ } catch (IOException ex) {
+ // Ignore
+ }
+ }
+ }
+ long dump = nCreateDump(fd.getInt$(), dumpProto);
+ try {
+ synchronized (mFileAccessLock) {
+ HashSet<File> skipList = dumpActiveLocked(dump, buffers);
+ buffers.clear();
+ dumpHistoricalLocked(dump, skipList);
+ }
+ } finally {
+ nFinishDump(dump);
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ nativeDestructor();
+ }
+
+ private native void nativeInit();
+ private static native void nativeDestructor();
+
+ private static native int nGetAshmemSize();
+ private static native long nCreateDump(int outFd, boolean isProto);
+ private static native void nAddToDump(long dump, String path, String packageName,
+ long versionCode, long startTime, long endTime, byte[] data);
+ private static native void nAddToDump(long dump, String path);
+ private static native void nFinishDump(long dump);
+ private static native void nFinishDumpInMemory(long dump, long pulledData, boolean lastFullDay);
+ private static native void nSaveBuffer(String path, String packageName, long versionCode,
+ long startTime, long endTime, byte[] data);
+
+ private final class BufferInfo {
+ final String mPackageName;
+ final long mVersionCode;
+ long mStartTime;
+ long mEndTime;
+
+ BufferInfo(String packageName, long versionCode, long startTime) {
+ this.mPackageName = packageName;
+ this.mVersionCode = versionCode;
+ this.mStartTime = startTime;
+ }
+ }
+
+ private final class ActiveBuffer implements DeathRecipient {
+ final BufferInfo mInfo;
+ final int mUid;
+ final int mPid;
+ final IGraphicsStatsCallback mCallback;
+ final IBinder mToken;
+ SharedMemory mProcessBuffer;
+ ByteBuffer mMapping;
+
+ ActiveBuffer(IGraphicsStatsCallback token, int uid, int pid, String packageName,
+ long versionCode)
+ throws RemoteException, IOException {
+ mInfo = new BufferInfo(packageName, versionCode, System.currentTimeMillis());
+ mUid = uid;
+ mPid = pid;
+ mCallback = token;
+ mToken = mCallback.asBinder();
+ mToken.linkToDeath(this, 0);
+ try {
+ mProcessBuffer = SharedMemory.create("GFXStats-" + pid, mAshmemSize);
+ mMapping = mProcessBuffer.mapReadWrite();
+ } catch (ErrnoException ex) {
+ ex.rethrowAsIOException();
+ }
+ mMapping.position(0);
+ mMapping.put(mZeroData, 0, mAshmemSize);
+ }
+
+ @Override
+ public void binderDied() {
+ mToken.unlinkToDeath(this, 0);
+ processDied(this);
+ }
+
+ void closeAllBuffers() {
+ if (mMapping != null) {
+ SharedMemory.unmap(mMapping);
+ mMapping = null;
+ }
+ if (mProcessBuffer != null) {
+ mProcessBuffer.close();
+ mProcessBuffer = null;
+ }
+ }
+
+ ParcelFileDescriptor getPfd() {
+ try {
+ return mProcessBuffer.getFdDup();
+ } catch (IOException ex) {
+ throw new IllegalStateException("Failed to get PFD from memory file", ex);
+ }
+ }
+
+ void readBytes(byte[] buffer, int count) throws IOException {
+ if (mMapping == null) {
+ throw new IOException("SharedMemory has been deactivated");
+ }
+ mMapping.position(0);
+ mMapping.get(buffer, 0, count);
+ }
+ }
+
+ private final class HistoricalBuffer {
+ final BufferInfo mInfo;
+ final byte[] mData = new byte[mAshmemSize];
+ HistoricalBuffer(ActiveBuffer active) throws IOException {
+ mInfo = active.mInfo;
+ mInfo.mEndTime = System.currentTimeMillis();
+ active.readBytes(mData, mAshmemSize);
+ }
+ }
+}
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index 3b864139cf71..d08bfcf45a5c 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -157,7 +157,7 @@ public class HardwareRenderer {
public HardwareRenderer() {
mRootNode = RenderNode.adopt(nCreateRootRenderNode());
mRootNode.setClipToBounds(false);
- mNativeProxy = nCreateProxy(!mOpaque, mRootNode.mNativeRenderNode);
+ mNativeProxy = nCreateProxy(!mOpaque, mIsWideGamut, mRootNode.mNativeRenderNode);
if (mNativeProxy == 0) {
throw new OutOfMemoryError("Unable to create hardware renderer");
}
@@ -1085,7 +1085,8 @@ public class HardwareRenderer {
private static native long nCreateRootRenderNode();
- private static native long nCreateProxy(boolean translucent, long rootRenderNode);
+ private static native long nCreateProxy(boolean translucent, boolean isWideGamut,
+ long rootRenderNode);
private static native void nDeleteProxy(long nativeProxy);
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index ba96a06cc852..4899fbe431cc 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -19,6 +19,7 @@ package android.graphics.fonts;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.os.LocaleList;
@@ -35,7 +36,9 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InputStream;
import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.util.Arrays;
import java.util.Objects;
@@ -54,10 +57,6 @@ public final class Font {
* A builder class for creating new Font.
*/
public static final class Builder {
- private static final NativeAllocationRegistry sAssetByteBufferRegistry =
- NativeAllocationRegistry.createMalloced(ByteBuffer.class.getClassLoader(),
- nGetReleaseNativeAssetFunc());
-
private static final NativeAllocationRegistry sFontRegistry =
NativeAllocationRegistry.createMalloced(Font.class.getClassLoader(),
nGetReleaseNativeFont());
@@ -151,7 +150,11 @@ public final class Font {
* @param path the file name of the font data in the asset directory
*/
public Builder(@NonNull AssetManager am, @NonNull String path) {
- this(am, path, true /* is asset */, 0 /* cookie */);
+ try {
+ mBuffer = createBuffer(am, path, true /* is asset */, 0 /* cookie */);
+ } catch (IOException e) {
+ mException = e;
+ }
}
/**
@@ -165,18 +168,11 @@ public final class Font {
*/
public Builder(@NonNull AssetManager am, @NonNull String path, boolean isAsset,
int cookie) {
- final long nativeAsset = nGetNativeAsset(am, path, isAsset, cookie);
- if (nativeAsset == 0) {
- mException = new FileNotFoundException("Unable to open " + path);
- return;
- }
- final ByteBuffer b = nGetAssetBuffer(nativeAsset);
- sAssetByteBufferRegistry.registerNativeAllocation(b, nativeAsset);
- if (b == null) {
- mException = new FileNotFoundException(path + " not found");
- return;
+ try {
+ mBuffer = createBuffer(am, path, isAsset, cookie);
+ } catch (IOException e) {
+ mException = e;
}
- mBuffer = b;
}
/**
@@ -199,19 +195,64 @@ public final class Font {
mException = new FileNotFoundException(resId + " must be font file.");
return;
}
- final long nativeAsset = nGetNativeAsset(res.getAssets(), str, false /* is asset */,
- value.assetCookie);
- if (nativeAsset == 0) {
- mException = new FileNotFoundException("Unable to open " + str);
- return;
+
+ try {
+ mBuffer = createBuffer(res.getAssets(), str, false, value.assetCookie);
+ } catch (IOException e) {
+ mException = e;
}
- final ByteBuffer b = nGetAssetBuffer(nativeAsset);
- sAssetByteBufferRegistry.registerNativeAllocation(b, nativeAsset);
- if (b == null) {
- mException = new FileNotFoundException(str + " not found");
- return;
+ }
+
+ /**
+ * Creates a buffer containing font data using the assetManager and other
+ * provided inputs.
+ *
+ * @param am the application's asset manager
+ * @param path the file name of the font data in the asset directory
+ * @param isAsset true if the undelying data is in asset
+ * @param cookie set asset cookie
+ * @return buffer containing the contents of the file
+ *
+ * @hide
+ */
+ public static ByteBuffer createBuffer(@NonNull AssetManager am, @NonNull String path,
+ boolean isAsset, int cookie) throws IOException {
+ Preconditions.checkNotNull(am, "assetManager can not be null");
+ Preconditions.checkNotNull(path, "path can not be null");
+
+ if (!isAsset) {
+ // Attempt to open as FD, which should work unless the asset is compressed
+ AssetFileDescriptor assetFD;
+ try {
+ if (cookie > 0) {
+ assetFD = am.openNonAssetFd(cookie, path);
+ } else {
+ assetFD = am.openNonAssetFd(path);
+ }
+
+ try (FileInputStream fis = assetFD.createInputStream()) {
+ final FileChannel fc = fis.getChannel();
+ return fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
+ }
+ } catch (IOException e) {
+ // failed to open as FD so now we will attempt to open as an input stream
+ }
+ }
+
+ try (InputStream assetStream = isAsset ? am.open(path, AssetManager.ACCESS_BUFFER)
+ : am.openNonAsset(cookie, path, AssetManager.ACCESS_BUFFER)) {
+
+ int capacity = assetStream.available();
+ ByteBuffer buffer = ByteBuffer.allocateDirect(capacity);
+ buffer.order(ByteOrder.nativeOrder());
+ assetStream.read(buffer.array(), buffer.arrayOffset(), assetStream.available());
+
+ if (assetStream.read() != -1) {
+ throw new IOException("Unable to access full contents of " + path);
+ }
+
+ return buffer;
}
- mBuffer = b;
}
/**
@@ -396,15 +437,6 @@ public final class Font {
}
/**
- * Native methods for accessing underlying buffer in Asset
- */
- private static native long nGetNativeAsset(
- @NonNull AssetManager am, @NonNull String path, boolean isAsset, int cookie);
- private static native ByteBuffer nGetAssetBuffer(long nativeAsset);
- @CriticalNative
- private static native long nGetReleaseNativeAssetFunc();
-
- /**
* Native methods for creating Font
*/
private static native long nInitBuilder();