diff options
Diffstat (limited to 'core')
40 files changed, 1290 insertions, 640 deletions
diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl index 8ab9ac3..474154b 100644 --- a/core/java/android/app/IUiAutomationConnection.aidl +++ b/core/java/android/app/IUiAutomationConnection.aidl @@ -38,10 +38,12 @@ interface IUiAutomationConnection { boolean injectInputEvent(in InputEvent event, boolean sync); boolean setRotation(int rotation); Bitmap takeScreenshot(int width, int height); - void shutdown(); boolean clearWindowContentFrameStats(int windowId); WindowContentFrameStats getWindowContentFrameStats(int windowId); void clearWindowAnimationFrameStats(); WindowAnimationFrameStats getWindowAnimationFrameStats(); void executeShellCommand(String command, in ParcelFileDescriptor fd); + + // Called from the system process. + oneway void shutdown(); } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index b0d8541..fc71783 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1862,6 +1862,9 @@ public class Notification implements Parcelable } else { sb.append("null"); } + if (this.tickerText != null) { + sb.append(" tick"); + } sb.append(" defaults=0x"); sb.append(Integer.toHexString(this.defaults)); sb.append(" flags=0x"); diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index 40126d6..ee0fc91 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -4,6 +4,7 @@ import android.app.Activity; import android.content.ComponentName; import android.graphics.Matrix; import android.graphics.Rect; +import android.os.BadParcelableException; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; @@ -31,8 +32,12 @@ public class AssistStructure implements Parcelable { static final String TAG = "AssistStructure"; static final boolean DEBUG_PARCEL = false; + static final boolean DEBUG_PARCEL_CHILDREN = false; static final boolean DEBUG_PARCEL_TREE = false; + static final int VALIDATE_WINDOW_TOKEN = 0x11111111; + static final int VALIDATE_VIEW_TOKEN = 0x22222222; + boolean mHaveData; ComponentName mActivityComponent; @@ -173,6 +178,26 @@ public class AssistStructure implements Parcelable { mCurViewStackEntry = entry; } + void writeView(ViewNode child, Parcel out, PooledStringWriter pwriter, int levelAdj) { + if (DEBUG_PARCEL) Log.d(TAG, "write view: at " + out.dataPosition() + + ", windows=" + mNumWrittenWindows + + ", views=" + mNumWrittenViews + + ", level=" + (mCurViewStackPos+levelAdj)); + out.writeInt(VALIDATE_VIEW_TOKEN); + int flags = child.writeSelfToParcel(out, pwriter, mTmpMatrix); + mNumWrittenViews++; + // If the child has children, push it on the stack to write them next. + if ((flags&ViewNode.FLAGS_HAS_CHILDREN) != 0) { + if (DEBUG_PARCEL_TREE || DEBUG_PARCEL_CHILDREN) Log.d(TAG, + "Preparing to write " + child.mChildren.length + + " children: @ #" + mNumWrittenViews + + ", level " + (mCurViewStackPos+levelAdj)); + out.writeInt(child.mChildren.length); + int pos = ++mCurViewStackPos; + pushViewStackEntry(child, pos); + } + } + boolean writeNextEntryToParcel(AssistStructure as, Parcel out, PooledStringWriter pwriter) { // Write next view node if appropriate. if (mCurViewStackEntry != null) { @@ -182,20 +207,7 @@ public class AssistStructure implements Parcelable { + mCurViewStackEntry.curChild + " in " + mCurViewStackEntry.node); ViewNode child = mCurViewStackEntry.node.mChildren[mCurViewStackEntry.curChild]; mCurViewStackEntry.curChild++; - if (DEBUG_PARCEL) Log.d(TAG, "write view: at " + out.dataPosition() - + ", windows=" + mNumWrittenWindows - + ", views=" + mNumWrittenViews); - out.writeInt(1); - int flags = child.writeSelfToParcel(out, pwriter, mTmpMatrix); - mNumWrittenViews++; - // If the child has children, push it on the stack to write them next. - if ((flags&ViewNode.FLAGS_HAS_CHILDREN) != 0) { - if (DEBUG_PARCEL_TREE) Log.d(TAG, "Preparing to write " - + child.mChildren.length + " children under " + child); - out.writeInt(child.mChildren.length); - int pos = ++mCurViewStackPos; - pushViewStackEntry(child, pos); - } + writeView(child, out, pwriter, 1); return true; } @@ -223,13 +235,13 @@ public class AssistStructure implements Parcelable { if (DEBUG_PARCEL) Log.d(TAG, "write window #" + pos + ": at " + out.dataPosition() + ", windows=" + mNumWrittenWindows + ", views=" + mNumWrittenViews); - out.writeInt(1); + out.writeInt(VALIDATE_WINDOW_TOKEN); win.writeSelfToParcel(out, pwriter, mTmpMatrix); mNumWrittenWindows++; ViewNode root = win.mRoot; mCurViewStackPos = 0; - if (DEBUG_PARCEL_TREE) Log.d(TAG, "Pushing initial root view " + root); - pushViewStackEntry(root, 0); + if (DEBUG_PARCEL_TREE) Log.d(TAG, "Writing initial root view " + root); + writeView(root, out, pwriter, 0); return true; } @@ -271,11 +283,16 @@ public class AssistStructure implements Parcelable { + ", views=" + mNumReadViews); } - Parcel readParcel() { + Parcel readParcel(int validateToken, int level) { if (DEBUG_PARCEL) Log.d(TAG, "readParcel: at " + mCurParcel.dataPosition() + ", avail=" + mCurParcel.dataAvail() + ", windows=" + mNumReadWindows - + ", views=" + mNumReadViews); - if (mCurParcel.readInt() != 0) { + + ", views=" + mNumReadViews + ", level=" + level); + int token = mCurParcel.readInt(); + if (token != 0) { + if (token != validateToken) { + throw new BadParcelableException("Got token " + Integer.toHexString(token) + + ", expected token " + Integer.toHexString(validateToken)); + } return mCurParcel; } // We have run out of partial data, need to read another batch. @@ -406,7 +423,7 @@ public class AssistStructure implements Parcelable { } WindowNode(ParcelTransferReader reader) { - Parcel in = reader.readParcel(); + Parcel in = reader.readParcel(VALIDATE_WINDOW_TOKEN, 0); reader.mNumReadWindows++; mX = in.readInt(); mY = in.readInt(); @@ -414,7 +431,7 @@ public class AssistStructure implements Parcelable { mHeight = in.readInt(); mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); mDisplayId = in.readInt(); - mRoot = new ViewNode(reader); + mRoot = new ViewNode(reader, 0); } void writeSelfToParcel(Parcel out, PooledStringWriter pwriter, float[] tmpMatrix) { @@ -548,8 +565,8 @@ public class AssistStructure implements Parcelable { ViewNode() { } - ViewNode(ParcelTransferReader reader) { - final Parcel in = reader.readParcel(); + ViewNode(ParcelTransferReader reader, int nestingLevel) { + final Parcel in = reader.readParcel(VALIDATE_VIEW_TOKEN, nestingLevel); reader.mNumReadViews++; final PooledStringReader preader = reader.mStringReader; mClassName = preader.readString(); @@ -604,9 +621,13 @@ public class AssistStructure implements Parcelable { } if ((flags&FLAGS_HAS_CHILDREN) != 0) { final int NCHILDREN = in.readInt(); + if (DEBUG_PARCEL_TREE || DEBUG_PARCEL_CHILDREN) Log.d(TAG, + "Preparing to read " + NCHILDREN + + " children: @ #" + reader.mNumReadViews + + ", level " + nestingLevel); mChildren = new ViewNode[NCHILDREN]; for (int i=0; i<NCHILDREN; i++) { - mChildren[i] = new ViewNode(reader); + mChildren[i] = new ViewNode(reader, nestingLevel + 1); } } } diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java index e09ab56..2ba8774 100644 --- a/core/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java @@ -79,6 +79,10 @@ public final class BluetoothLeScanner { * delivered through {@code callback}. * <p> * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * An app must hold + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or + * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission + * in order to get results. * * @param callback Callback used to deliver scan results. * @throws IllegalArgumentException If {@code callback} is null. @@ -95,6 +99,10 @@ public final class BluetoothLeScanner { * Start Bluetooth LE scan. The scan results will be delivered through {@code callback}. * <p> * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * An app must hold + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or + * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission + * in order to get results. * * @param filters {@link ScanFilter}s for finding exact BLE devices. * @param settings Settings for the scan. diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 3cc7684..ba9cf7c 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -16,8 +16,11 @@ package android.content; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.Manifest.permission.INTERACT_ACROSS_USERS; +import static android.app.AppOpsManager.MODE_ALLOWED; +import static android.app.AppOpsManager.MODE_ERRORED; +import static android.app.AppOpsManager.MODE_IGNORED; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; import android.annotation.NonNull; import android.annotation.Nullable; @@ -40,8 +43,8 @@ import android.os.OperationCanceledException; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.UserHandle; -import android.util.Log; import android.text.TextUtils; +import android.util.Log; import java.io.File; import java.io.FileDescriptor; @@ -474,14 +477,9 @@ public abstract class ContentProvider implements ComponentCallbacks2 { private int enforceReadPermission(String callingPkg, Uri uri, IBinder callerToken) throws SecurityException { - enforceReadPermissionInner(uri, callerToken); - - final int permOp = AppOpsManager.permissionToOpCode(mReadPermission); - if (permOp != AppOpsManager.OP_NONE) { - final int mode = mAppOpsManager.noteProxyOp(permOp, callingPkg); - if (mode != AppOpsManager.MODE_ALLOWED) { - return mode; - } + final int mode = enforceReadPermissionInner(uri, callingPkg, callerToken); + if (mode != MODE_ALLOWED) { + return mode; } if (mReadOp != AppOpsManager.OP_NONE) { @@ -493,14 +491,9 @@ public abstract class ContentProvider implements ComponentCallbacks2 { private int enforceWritePermission(String callingPkg, Uri uri, IBinder callerToken) throws SecurityException { - enforceWritePermissionInner(uri, callerToken); - - final int permOp = AppOpsManager.permissionToOpCode(mWritePermission); - if (permOp != AppOpsManager.OP_NONE) { - final int mode = mAppOpsManager.noteProxyOp(permOp, callingPkg); - if (mode != AppOpsManager.MODE_ALLOWED) { - return mode; - } + final int mode = enforceWritePermissionInner(uri, callingPkg, callerToken); + if (mode != MODE_ALLOWED) { + return mode; } if (mWriteOp != AppOpsManager.OP_NONE) { @@ -518,26 +511,47 @@ public abstract class ContentProvider implements ComponentCallbacks2 { == PERMISSION_GRANTED; } + /** + * Verify that calling app holds both the given permission and any app-op + * associated with that permission. + */ + private int checkPermissionAndAppOp(String permission, String callingPkg, + IBinder callerToken) { + if (getContext().checkPermission(permission, Binder.getCallingPid(), Binder.getCallingUid(), + callerToken) != PERMISSION_GRANTED) { + return MODE_ERRORED; + } + + final int permOp = AppOpsManager.permissionToOpCode(permission); + if (permOp != AppOpsManager.OP_NONE) { + return mTransport.mAppOpsManager.noteProxyOp(permOp, callingPkg); + } + + return MODE_ALLOWED; + } + /** {@hide} */ - protected void enforceReadPermissionInner(Uri uri, IBinder callerToken) + protected int enforceReadPermissionInner(Uri uri, String callingPkg, IBinder callerToken) throws SecurityException { final Context context = getContext(); final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); String missingPerm = null; + int strongestMode = MODE_ALLOWED; if (UserHandle.isSameApp(uid, mMyUid)) { - return; + return MODE_ALLOWED; } if (mExported && checkUser(pid, uid, context)) { final String componentPerm = getReadPermission(); if (componentPerm != null) { - if (context.checkPermission(componentPerm, pid, uid, callerToken) - == PERMISSION_GRANTED) { - return; + final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, callerToken); + if (mode == MODE_ALLOWED) { + return MODE_ALLOWED; } else { missingPerm = componentPerm; + strongestMode = Math.max(strongestMode, mode); } } @@ -551,14 +565,15 @@ public abstract class ContentProvider implements ComponentCallbacks2 { for (PathPermission pp : pps) { final String pathPerm = pp.getReadPermission(); if (pathPerm != null && pp.match(path)) { - if (context.checkPermission(pathPerm, pid, uid, callerToken) - == PERMISSION_GRANTED) { - return; + final int mode = checkPermissionAndAppOp(pathPerm, callingPkg, callerToken); + if (mode == MODE_ALLOWED) { + return MODE_ALLOWED; } else { // any denied <path-permission> means we lose // default <provider> access. allowDefaultRead = false; missingPerm = pathPerm; + strongestMode = Math.max(strongestMode, mode); } } } @@ -566,7 +581,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { // if we passed <path-permission> checks above, and no default // <provider> permission, then allow access. - if (allowDefaultRead) return; + if (allowDefaultRead) return MODE_ALLOWED; } // last chance, check against any uri grants @@ -575,7 +590,13 @@ public abstract class ContentProvider implements ComponentCallbacks2 { ? maybeAddUserId(uri, callingUserId) : uri; if (context.checkUriPermission(userUri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION, callerToken) == PERMISSION_GRANTED) { - return; + return MODE_ALLOWED; + } + + // If the worst denial we found above was ignored, then pass that + // ignored through; otherwise we assume it should be a real error below. + if (strongestMode == MODE_IGNORED) { + return MODE_IGNORED; } final String failReason = mExported @@ -587,25 +608,27 @@ public abstract class ContentProvider implements ComponentCallbacks2 { } /** {@hide} */ - protected void enforceWritePermissionInner(Uri uri, IBinder callerToken) + protected int enforceWritePermissionInner(Uri uri, String callingPkg, IBinder callerToken) throws SecurityException { final Context context = getContext(); final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); String missingPerm = null; + int strongestMode = MODE_ALLOWED; if (UserHandle.isSameApp(uid, mMyUid)) { - return; + return MODE_ALLOWED; } if (mExported && checkUser(pid, uid, context)) { final String componentPerm = getWritePermission(); if (componentPerm != null) { - if (context.checkPermission(componentPerm, pid, uid, callerToken) - == PERMISSION_GRANTED) { - return; + final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, callerToken); + if (mode == MODE_ALLOWED) { + return MODE_ALLOWED; } else { missingPerm = componentPerm; + strongestMode = Math.max(strongestMode, mode); } } @@ -619,14 +642,15 @@ public abstract class ContentProvider implements ComponentCallbacks2 { for (PathPermission pp : pps) { final String pathPerm = pp.getWritePermission(); if (pathPerm != null && pp.match(path)) { - if (context.checkPermission(pathPerm, pid, uid, callerToken) - == PERMISSION_GRANTED) { - return; + final int mode = checkPermissionAndAppOp(pathPerm, callingPkg, callerToken); + if (mode == MODE_ALLOWED) { + return MODE_ALLOWED; } else { // any denied <path-permission> means we lose // default <provider> access. allowDefaultWrite = false; missingPerm = pathPerm; + strongestMode = Math.max(strongestMode, mode); } } } @@ -634,13 +658,19 @@ public abstract class ContentProvider implements ComponentCallbacks2 { // if we passed <path-permission> checks above, and no default // <provider> permission, then allow access. - if (allowDefaultWrite) return; + if (allowDefaultWrite) return MODE_ALLOWED; } // last chance, check against any uri grants if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, callerToken) == PERMISSION_GRANTED) { - return; + return MODE_ALLOWED; + } + + // If the worst denial we found above was ignored, then pass that + // ignored through; otherwise we assume it should be a real error below. + if (strongestMode == MODE_IGNORED) { + return MODE_IGNORED; } final String failReason = mExported diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index ceb610a..bc24d67 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -253,7 +253,7 @@ interface IPackageManager { List<PackageInfo> getPreferredPackages(int flags); - void resetPreferredActivities(int userId); + void resetApplicationPreferences(int userId); ResolveInfo getLastChosenActivity(in Intent intent, String resolvedType, int flags); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 6533bbc..cda5816 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1514,6 +1514,13 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device has biometric hardware to detect a fingerprint. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_FINGERPRINT = "android.hardware.fingerprint"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device supports portrait orientation * screens. For backwards compatibility, you can assume that if neither * this nor {@link #FEATURE_SCREEN_LANDSCAPE} is set then the device supports diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index 864225a..af4c2bc 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -16,6 +16,7 @@ package android.os; +import android.annotation.NonNull; import android.provider.DocumentsContract.Document; import android.system.ErrnoException; import android.system.Os; @@ -69,6 +70,8 @@ public class FileUtils { /** Regular expression for safe filenames: no spaces or metacharacters */ private static final Pattern SAFE_FILENAME_PATTERN = Pattern.compile("[\\w%+,./=_-]+"); + private static final File[] EMPTY = new File[0]; + /** * Set owner and mode of of given {@link File}. * @@ -634,4 +637,13 @@ public class FileUtils { return new File(parent, name + "." + ext); } } + + public static @NonNull File[] listFilesOrEmpty(File dir) { + File[] res = dir.listFiles(); + if (res != null) { + return res; + } else { + return EMPTY; + } + } } diff --git a/core/java/android/os/IDeviceIdleController.aidl b/core/java/android/os/IDeviceIdleController.aidl index b768852..d3eec1e 100644 --- a/core/java/android/os/IDeviceIdleController.aidl +++ b/core/java/android/os/IDeviceIdleController.aidl @@ -29,5 +29,6 @@ interface IDeviceIdleController { boolean isPowerSaveWhitelistApp(String name); void addPowerSaveTempWhitelistApp(String name, long duration, int userId, String reason); long addPowerSaveTempWhitelistAppForMms(String name, int userId, String reason); + long addPowerSaveTempWhitelistAppForSms(String name, int userId, String reason); void exitIdle(String reason); } diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java index 6979bee..5a341fc 100644 --- a/core/java/android/provider/DocumentsProvider.java +++ b/core/java/android/provider/DocumentsProvider.java @@ -640,7 +640,7 @@ public abstract class DocumentsProvider extends ContentProvider { final Bundle out = new Bundle(); try { if (METHOD_CREATE_DOCUMENT.equals(method)) { - enforceWritePermissionInner(documentUri, null); + enforceWritePermissionInner(documentUri, getCallingPackage(), null); final String mimeType = extras.getString(Document.COLUMN_MIME_TYPE); final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME); @@ -654,7 +654,7 @@ public abstract class DocumentsProvider extends ContentProvider { out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri); } else if (METHOD_RENAME_DOCUMENT.equals(method)) { - enforceWritePermissionInner(documentUri, null); + enforceWritePermissionInner(documentUri, getCallingPackage(), null); final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME); final String newDocumentId = renameDocument(documentId, displayName); @@ -678,7 +678,7 @@ public abstract class DocumentsProvider extends ContentProvider { } } else if (METHOD_DELETE_DOCUMENT.equals(method)) { - enforceWritePermissionInner(documentUri, null); + enforceWritePermissionInner(documentUri, getCallingPackage(), null); deleteDocument(documentId); // Document no longer exists, clean up any grants diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index fff355b..a79970c 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -7578,14 +7578,6 @@ public final class Settings { public static final String WFC_IMS_ROAMING_ENABLED = "wfc_ims_roaming_enabled"; /** - * Global override to disable VoLTE (independent of user setting) - * <p> - * Type: int (1 for disable VoLTE, 0 to use user configuration) - * @hide - */ - public static final String VOLTE_FEATURE_DISABLED = "volte_feature_disabled"; - - /** * Whether user can enable/disable LTE as a preferred network. A carrier might control * this via gservices, OMA-DM, carrier app, etc. * <p> diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java index 1ee3827..10a994a 100644 --- a/core/java/android/text/Hyphenator.java +++ b/core/java/android/text/Hyphenator.java @@ -45,6 +45,8 @@ public class Hyphenator { @GuardedBy("sLock") final static HashMap<Locale, Hyphenator> sMap = new HashMap<Locale, Hyphenator>(); + final static Hyphenator sEmptyHyphenator = new Hyphenator(StaticLayout.nLoadHyphenator("")); + final private long mNativePtr; private Hyphenator(long nativePtr) { @@ -53,19 +55,19 @@ public class Hyphenator { public static long get(@Nullable Locale locale) { synchronized (sLock) { - if (sMap.containsKey(locale)) { - Hyphenator result = sMap.get(locale); - return (result == null) ? 0 : result.mNativePtr; + Hyphenator result = sMap.get(locale); + if (result != null) { + return result.mNativePtr; } // TODO: Convert this a proper locale-fallback system // Fall back to language-only, if available Locale languageOnlyLocale = new Locale(locale.getLanguage()); - if (sMap.containsKey(languageOnlyLocale)) { - Hyphenator result = sMap.get(languageOnlyLocale); + result = sMap.get(languageOnlyLocale); + if (result != null) { sMap.put(locale, result); - return (result == null) ? 0 : result.mNativePtr; + return result.mNativePtr; } // Fall back to script-only, if available @@ -75,16 +77,16 @@ public class Hyphenator { .setLanguage("und") .setScript(script) .build(); - if (sMap.containsKey(scriptOnlyLocale)) { - Hyphenator result = sMap.get(scriptOnlyLocale); + result = sMap.get(scriptOnlyLocale); + if (result != null) { sMap.put(locale, result); - return (result == null) ? 0 : result.mNativePtr; + return result.mNativePtr; } } - sMap.put(locale, null); // To remember we found nothing. + sMap.put(locale, sEmptyHyphenator); // To remember we found nothing. } - return 0; + return sEmptyHyphenator.mNativePtr; } private static Hyphenator loadHyphenator(String languageTag) { diff --git a/core/java/android/util/LocalLog.java b/core/java/android/util/LocalLog.java index 39f66a5..a405dab 100644 --- a/core/java/android/util/LocalLog.java +++ b/core/java/android/util/LocalLog.java @@ -30,20 +30,32 @@ public final class LocalLog { private LinkedList<String> mLog; private int mMaxLines; private long mNow; + private final boolean mKeepFirst; public LocalLog(int maxLines) { mLog = new LinkedList<String>(); mMaxLines = maxLines; + mKeepFirst = false; + } + + public LocalLog(int maxLines, boolean keepFirst) { + mLog = new LinkedList<String>(); + mMaxLines = maxLines; + mKeepFirst = keepFirst; } public synchronized void log(String msg) { + mNow = System.currentTimeMillis(); + StringBuilder sb = new StringBuilder(); + Calendar c = Calendar.getInstance(); + c.setTimeInMillis(mNow); + sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c)); + logStraight(sb.toString() + " - " + msg); + } + + private synchronized void logStraight(String msg) { + if (mKeepFirst == false || mLog.size() < mMaxLines) mLog.add(msg); if (mMaxLines > 0) { - mNow = System.currentTimeMillis(); - StringBuilder sb = new StringBuilder(); - Calendar c = Calendar.getInstance(); - c.setTimeInMillis(mNow); - sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c)); - mLog.add(sb.toString() + " - " + msg); while (mLog.size() > mMaxLines) mLog.remove(); } } @@ -74,4 +86,13 @@ public final class LocalLog { public ReadOnlyLocalLog readOnlyLocalLog() { return new ReadOnlyLocalLog(this); } + + public synchronized void copyTo(LocalLog other, int lines) { + int end = mLog.size()-1; + int begin = end - lines; + if (begin < 0) begin = 0; + for (; begin < end; begin++) { + other.logStraight(mLog.get(begin)); + } + } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 9b1db57..c22c0ef 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -555,11 +555,6 @@ public final class ViewRootImpl implements ViewParent, mPendingContentInsets.set(mAttachInfo.mContentInsets); mPendingStableInsets.set(mAttachInfo.mStableInsets); mPendingVisibleInsets.set(0, 0, 0, 0); - try { - relayoutWindow(attrs, getHostVisibility(), false); - } catch (RemoteException e) { - if (DEBUG_LAYOUT) Log.e(TAG, "failed to relayoutWindow", e); - } if (DEBUG_LAYOUT) Log.v(TAG, "Added window " + mWindow); if (res < WindowManagerGlobal.ADD_OKAY) { mAttachInfo.mRootView = null; diff --git a/core/java/android/view/inputmethod/InputMethodManagerInternal.java b/core/java/android/view/inputmethod/InputMethodManagerInternal.java new file mode 100644 index 0000000..c22127b --- /dev/null +++ b/core/java/android/view/inputmethod/InputMethodManagerInternal.java @@ -0,0 +1,30 @@ +/* + * 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.view.inputmethod; + +/** + * Input method manager local system service interface. + * + * @hide Only for use within the system server. + */ +public interface InputMethodManagerInternal { + /** + * Called by the power manager to tell the input method manager whether it + * should start watching for wake events. + */ + public void setInteractive(boolean interactive); +} diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java index 0b18bb8..4737e9b 100644 --- a/core/java/android/webkit/WebChromeClient.java +++ b/core/java/android/webkit/WebChromeClient.java @@ -284,19 +284,13 @@ public class WebChromeClient { * currently set for that origin. The host application should invoke the * specified callback with the desired permission state. See * {@link GeolocationPermissions} for details. - * - * If this method isn't overridden, the callback is invoked with permission - * denied state. - * * @param origin The origin of the web content attempting to use the * Geolocation API. * @param callback The callback to use to set the permission state for the * origin. */ public void onGeolocationPermissionsShowPrompt(String origin, - GeolocationPermissions.Callback callback) { - callback.invoke(origin, false, false); - } + GeolocationPermissions.Callback callback) {} /** * Notify the host application that a request for Geolocation permissions, diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java index e3ce6f2..a12b15e 100644 --- a/core/java/android/widget/AppSecurityPermissions.java +++ b/core/java/android/widget/AppSecurityPermissions.java @@ -533,6 +533,12 @@ public class AppSecurityPermissions { int existingReqFlags) { final int base = pInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE; final boolean isNormal = (base == PermissionInfo.PROTECTION_NORMAL); + + // We do not show normal permissions in the UI. + if (isNormal) { + return false; + } + final boolean isDangerous = (base == PermissionInfo.PROTECTION_DANGEROUS) || ((pInfo.protectionLevel&PermissionInfo.PROTECTION_FLAG_PRE23) != 0); final boolean isRequired = @@ -546,7 +552,7 @@ public class AppSecurityPermissions { // Dangerous and normal permissions are always shown to the user if the permission // is required, or it was previously granted - if ((isNormal || isDangerous) && (isRequired || wasGranted || isGranted)) { + if (isDangerous && (isRequired || wasGranted || isGranted)) { return true; } diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 15d13ae..010cb27 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -1004,7 +1004,8 @@ public class Editor { } if (!handled && mTextActionMode != null) { - if (touchPositionIsInSelection()) { + // TODO: Fix dragging in extracted mode. + if (touchPositionIsInSelection() && !mTextView.isInExtractedMode()) { // Start a drag final int start = mTextView.getSelectionStart(); final int end = mTextView.getSelectionEnd(); @@ -4059,9 +4060,17 @@ public class Editor { private float mPrevX; // Indicates if the handle has moved a boundary between LTR and RTL text. private boolean mLanguageDirectionChanged = false; + // Distance from edge of horizontally scrolling text view + // to use to switch to character mode. + private final float mTextViewEdgeSlop; + // Used to save text view location. + private final int[] mTextViewLocation = new int[2]; public SelectionStartHandleView(Drawable drawableLtr, Drawable drawableRtl) { super(drawableLtr, drawableRtl); + ViewConfiguration viewConfiguration = ViewConfiguration.get( + mTextView.getContext()); + mTextViewEdgeSlop = viewConfiguration.getScaledTouchSlop() * 4; } @Override @@ -4099,7 +4108,7 @@ public class Editor { if (layout == null) { // HandleView will deal appropriately in positionAtCursorOffset when // layout is null. - positionAtCursorOffset(mTextView.getOffsetForPosition(x, y), false); + positionAndAdjustForCrossingHandles(mTextView.getOffsetForPosition(x, y)); return; } @@ -4141,12 +4150,12 @@ public class Editor { // to the current position. mLanguageDirectionChanged = true; mTouchWordDelta = 0.0f; - positionAtCursorOffset(offset, false); + positionAndAdjustForCrossingHandles(offset); return; } else if (mLanguageDirectionChanged && !isLvlBoundary) { // We've just moved past the boundary so update the position. After this we can // figure out if the user is expanding or shrinking to go by word or character. - positionAtCursorOffset(offset, false); + positionAndAdjustForCrossingHandles(offset); mTouchWordDelta = 0.0f; mLanguageDirectionChanged = false; return; @@ -4159,6 +4168,21 @@ public class Editor { } } + if (mTextView.getHorizontallyScrolling()) { + if (positionNearEdgeOfScrollingView(x, atRtl) + && (mTextView.getScrollX() != 0) + && ((isExpanding && offset < selectionStart) || !isExpanding)) { + // If we're expanding ensure that the offset is smaller than the + // selection start, if the handle snapped to the word, the finger position + // may be out of sync and we don't want the selection to jump back. + mTouchWordDelta = 0.0f; + final int nextOffset = atRtl ? layout.getOffsetToRightOf(mPreviousOffset) + : layout.getOffsetToLeftOf(mPreviousOffset); + positionAndAdjustForCrossingHandles(nextOffset); + return; + } + } + if (isExpanding) { // User is increasing the selection. if (!mInWord || currLine < mPrevLine) { @@ -4214,17 +4238,22 @@ public class Editor { } if (positionCursor) { - // Handles can not cross and selection is at least one character. - if (offset >= selectionEnd) { - offset = getNextCursorOffset(selectionEnd, false); - mTouchWordDelta = 0.0f; - } mPreviousLineTouched = currLine; - positionAtCursorOffset(offset, false); + positionAndAdjustForCrossingHandles(offset); } mPrevX = x; } + private void positionAndAdjustForCrossingHandles(int offset) { + final int selectionEnd = mTextView.getSelectionEnd(); + if (offset >= selectionEnd) { + // Handles can not cross and selection is at least one character. + offset = getNextCursorOffset(selectionEnd, false); + mTouchWordDelta = 0.0f; + } + positionAtCursorOffset(offset, false); + } + @Override protected void positionAtCursorOffset(int offset, boolean parentScrolled) { super.positionAtCursorOffset(offset, parentScrolled); @@ -4242,6 +4271,20 @@ public class Editor { } return superResult; } + + private boolean positionNearEdgeOfScrollingView(float x, boolean atRtl) { + mTextView.getLocationOnScreen(mTextViewLocation); + boolean nearEdge; + if (atRtl) { + int rightEdge = mTextViewLocation[0] + mTextView.getWidth() + - mTextView.getPaddingRight(); + nearEdge = x > rightEdge - mTextViewEdgeSlop; + } else { + int leftEdge = mTextViewLocation[0] + mTextView.getPaddingLeft(); + nearEdge = x < leftEdge + mTextViewEdgeSlop; + } + return nearEdge; + } } private class SelectionEndHandleView extends HandleView { @@ -4253,9 +4296,17 @@ public class Editor { private float mPrevX; // Indicates if the handle has moved a boundary between LTR and RTL text. private boolean mLanguageDirectionChanged = false; + // Distance from edge of horizontally scrolling text view + // to use to switch to character mode. + private final float mTextViewEdgeSlop; + // Used to save the text view location. + private final int[] mTextViewLocation = new int[2]; public SelectionEndHandleView(Drawable drawableLtr, Drawable drawableRtl) { super(drawableLtr, drawableRtl); + ViewConfiguration viewConfiguration = ViewConfiguration.get( + mTextView.getContext()); + mTextViewEdgeSlop = viewConfiguration.getScaledTouchSlop() * 4; } @Override @@ -4293,7 +4344,7 @@ public class Editor { if (layout == null) { // HandleView will deal appropriately in positionAtCursorOffset when // layout is null. - positionAtCursorOffset(mTextView.getOffsetForPosition(x, y), false); + positionAndAdjustForCrossingHandles(mTextView.getOffsetForPosition(x, y)); return; } @@ -4335,12 +4386,12 @@ public class Editor { // to the current position. mLanguageDirectionChanged = true; mTouchWordDelta = 0.0f; - positionAtCursorOffset(offset, false); + positionAndAdjustForCrossingHandles(offset); return; } else if (mLanguageDirectionChanged && !isLvlBoundary) { // We've just moved past the boundary so update the position. After this we can // figure out if the user is expanding or shrinking to go by word or character. - positionAtCursorOffset(offset, false); + positionAndAdjustForCrossingHandles(offset); mTouchWordDelta = 0.0f; mLanguageDirectionChanged = false; return; @@ -4353,6 +4404,21 @@ public class Editor { } } + if (mTextView.getHorizontallyScrolling()) { + if (positionNearEdgeOfScrollingView(x, atRtl) + && mTextView.canScrollHorizontally(atRtl ? -1 : 1) + && ((isExpanding && offset > selectionEnd) || !isExpanding)) { + // If we're expanding ensure that the offset is actually greater than the + // selection end, if the handle snapped to the word, the finger position + // may be out of sync and we don't want the selection to jump back. + mTouchWordDelta = 0.0f; + final int nextOffset = atRtl ? layout.getOffsetToLeftOf(mPreviousOffset) + : layout.getOffsetToRightOf(mPreviousOffset); + positionAndAdjustForCrossingHandles(nextOffset); + return; + } + } + if (isExpanding) { // User is increasing the selection. if (!mInWord || currLine > mPrevLine) { @@ -4408,17 +4474,22 @@ public class Editor { } if (positionCursor) { - // Handles can not cross and selection is at least one character. - if (offset <= selectionStart) { - offset = getNextCursorOffset(selectionStart, true); - mTouchWordDelta = 0.0f; - } mPreviousLineTouched = currLine; - positionAtCursorOffset(offset, false); + positionAndAdjustForCrossingHandles(offset); } mPrevX = x; } + private void positionAndAdjustForCrossingHandles(int offset) { + final int selectionStart = mTextView.getSelectionStart(); + if (offset <= selectionStart) { + // Handles can not cross and selection is at least one character. + offset = getNextCursorOffset(selectionStart, true); + mTouchWordDelta = 0.0f; + } + positionAtCursorOffset(offset, false); + } + @Override protected void positionAtCursorOffset(int offset, boolean parentScrolled) { super.positionAtCursorOffset(offset, parentScrolled); @@ -4436,6 +4507,20 @@ public class Editor { } return superResult; } + + private boolean positionNearEdgeOfScrollingView(float x, boolean atRtl) { + mTextView.getLocationOnScreen(mTextViewLocation); + boolean nearEdge; + if (atRtl) { + int leftEdge = mTextViewLocation[0] + mTextView.getPaddingLeft(); + nearEdge = x < leftEdge + mTextViewEdgeSlop; + } else { + int rightEdge = mTextViewLocation[0] + mTextView.getWidth() + - mTextView.getPaddingRight(); + nearEdge = x > rightEdge - mTextViewEdgeSlop; + } + return nearEdge; + } } private int getCurrentLineAdjustedForSlop(Layout layout, int prevLine, float y) { diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index dac02fa..6a561e6 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -731,7 +731,8 @@ public class RelativeLayout extends ViewGroup { // Negative values in a mySize value in RelativeLayout // measurement is code for, "we got an unspecified mode in the // RelativeLayout's measure spec." - if (mySize < 0 && !mAllowBrokenMeasureSpecs) { + final boolean isUnspecified = mySize < 0; + if (isUnspecified && !mAllowBrokenMeasureSpecs) { if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) { // Constraints fixed both edges, so child has an exact size. childSpecSize = Math.max(0, childEnd - childStart); @@ -767,7 +768,7 @@ public class RelativeLayout extends ViewGroup { if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) { // Constraints fixed both edges, so child must be an exact size. - childSpecMode = MeasureSpec.EXACTLY; + childSpecMode = isUnspecified ? MeasureSpec.UNSPECIFIED : MeasureSpec.EXACTLY; childSpecSize = Math.max(0, maxAvailable); } else { if (childSize >= 0) { @@ -784,7 +785,7 @@ public class RelativeLayout extends ViewGroup { } else if (childSize == LayoutParams.MATCH_PARENT) { // Child wanted to be as big as possible. Give all available // space. - childSpecMode = MeasureSpec.EXACTLY; + childSpecMode = isUnspecified ? MeasureSpec.UNSPECIFIED : MeasureSpec.EXACTLY; childSpecSize = Math.max(0, maxAvailable); } else if (childSize == LayoutParams.WRAP_CONTENT) { // Child wants to wrap content. Use AT_MOST to communicate diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java index cbe535f..91ae27b 100644 --- a/core/java/com/android/internal/logging/MetricsLogger.java +++ b/core/java/com/android/internal/logging/MetricsLogger.java @@ -27,7 +27,20 @@ import android.view.View; */ public class MetricsLogger implements MetricsConstants { // Temporary constants go here, to await migration to MetricsConstants. - // next value is 227; + // next value is 238; + + public static final int TUNER = 227; + public static final int TUNER_QS = 228; + public static final int TUNER_DEMO_MODE = 229; + + public static final int TUNER_QS_REORDER = 230; + public static final int TUNER_QS_ADD = 231; + public static final int TUNER_QS_REMOVE = 232; + public static final int TUNER_STATUS_BAR_ENABLE = 233; + public static final int TUNER_STATUS_BAR_DISABLE = 234; + public static final int TUNER_DEMO_MODE_ENABLED = 235; + public static final int TUNER_DEMO_MODE_ON = 236; + public static final int TUNER_BATTERY_PERCENTAGE = 237; public static void visible(Context context, int category) throws IllegalArgumentException { if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) { diff --git a/core/java/com/android/internal/midi/MidiConstants.java b/core/java/com/android/internal/midi/MidiConstants.java index c13e5fc..b6b8bf0 100644 --- a/core/java/com/android/internal/midi/MidiConstants.java +++ b/core/java/com/android/internal/midi/MidiConstants.java @@ -55,18 +55,30 @@ public final class MidiConstants { public final static int SYSTEM_BYTE_LENGTHS[] = { 1, 2, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; - /********************************************************************/ - public static int getBytesPerMessage(int command) { - if ((command < 0x80) || (command > 0xFF)) { - return 0; - } else if (command >= 0xF0) { - return SYSTEM_BYTE_LENGTHS[command & 0x0F]; + /** + * MIDI messages, except for SysEx, are 1,2 or 3 bytes long. + * You can tell how long a MIDI message is from the first status byte. + * Do not call this for SysEx, which has variable length. + * @param statusByte + * @return number of bytes in a complete message or zero if data byte passed + */ + public static int getBytesPerMessage(byte statusByte) { + // Java bytes are signed so we need to mask off the high bits + // to get a value between 0 and 255. + int statusInt = statusByte & 0xFF; + if (statusInt >= 0xF0) { + // System messages use low nibble for size. + return SYSTEM_BYTE_LENGTHS[statusInt & 0x0F]; + } else if(statusInt >= 0x80) { + // Channel voice messages use high nibble for size. + return CHANNEL_BYTE_LENGTHS[(statusInt >> 4) - 8]; } else { - return CHANNEL_BYTE_LENGTHS[(command >> 4) - 8]; + return 0; // data byte } } + /** * @param msg * @param offset diff --git a/core/java/com/android/internal/midi/MidiFramer.java b/core/java/com/android/internal/midi/MidiFramer.java index 058f57c..62517fa 100644 --- a/core/java/com/android/internal/midi/MidiFramer.java +++ b/core/java/com/android/internal/midi/MidiFramer.java @@ -17,7 +17,7 @@ package com.android.internal.midi; import android.media.midi.MidiReceiver; -import android.util.Log; +//import android.util.Log; import java.io.IOException; @@ -37,7 +37,7 @@ public class MidiFramer extends MidiReceiver { private MidiReceiver mReceiver; private byte[] mBuffer = new byte[3]; private int mCount; - private int mRunningStatus; + private byte mRunningStatus; private int mNeeded; private boolean mInSysEx; @@ -59,22 +59,22 @@ public class MidiFramer extends MidiReceiver { @Override public void onSend(byte[] data, int offset, int count, long timestamp) throws IOException { - // Log.i(TAG, formatMidiData(data, offset, count)); int sysExStartOffset = (mInSysEx ? offset : -1); for (int i = 0; i < count; i++) { - int b = data[offset] & 0xFF; - if (b >= 0x80) { // status byte? - if (b < 0xF0) { // channel message? - mRunningStatus = (byte) b; + final byte currentByte = data[offset]; + final int currentInt = currentByte & 0xFF; + if (currentInt >= 0x80) { // status byte? + if (currentInt < 0xF0) { // channel message? + mRunningStatus = currentByte; mCount = 1; - mNeeded = MidiConstants.getBytesPerMessage(b) - 1; - } else if (b < 0xF8) { // system common? - if (b == 0xF0 /* SysEx Start */) { + mNeeded = MidiConstants.getBytesPerMessage(currentByte) - 1; + } else if (currentInt < 0xF8) { // system common? + if (currentInt == 0xF0 /* SysEx Start */) { // Log.i(TAG, "SysEx Start"); mInSysEx = true; sysExStartOffset = offset; - } else if (b == 0xF7 /* SysEx End */) { + } else if (currentInt == 0xF7 /* SysEx End */) { // Log.i(TAG, "SysEx End"); if (mInSysEx) { mReceiver.send(data, sysExStartOffset, @@ -83,10 +83,10 @@ public class MidiFramer extends MidiReceiver { sysExStartOffset = -1; } } else { - mBuffer[0] = (byte) b; + mBuffer[0] = currentByte; mRunningStatus = 0; mCount = 1; - mNeeded = MidiConstants.getBytesPerMessage(b) - 1; + mNeeded = MidiConstants.getBytesPerMessage(currentByte) - 1; } } else { // real-time? // Single byte message interleaved with other data. @@ -98,12 +98,11 @@ public class MidiFramer extends MidiReceiver { mReceiver.send(data, offset, 1, timestamp); } } else { // data byte - // Save SysEx data for SysEx End marker or end of buffer. if (!mInSysEx) { - mBuffer[mCount++] = (byte) b; + mBuffer[mCount++] = currentByte; if (--mNeeded == 0) { if (mRunningStatus != 0) { - mBuffer[0] = (byte) mRunningStatus; + mBuffer[0] = mRunningStatus; } mReceiver.send(mBuffer, 0, mCount, timestamp); mNeeded = MidiConstants.getBytesPerMessage(mBuffer[0]) - 1; diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java index 264b8c1..9caf78a 100644 --- a/core/java/com/android/internal/os/BatteryStatsHelper.java +++ b/core/java/com/android/internal/os/BatteryStatsHelper.java @@ -47,6 +47,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Locale; /** * A helper class for retrieving the power usage information for all applications and services. @@ -267,15 +268,20 @@ public final class BatteryStatsHelper { public static String makemAh(double power) { if (power == 0) return "0"; - else if (power < .00001) return String.format("%.8f", power); - else if (power < .0001) return String.format("%.7f", power); - else if (power < .001) return String.format("%.6f", power); - else if (power < .01) return String.format("%.5f", power); - else if (power < .1) return String.format("%.4f", power); - else if (power < 1) return String.format("%.3f", power); - else if (power < 10) return String.format("%.2f", power); - else if (power < 100) return String.format("%.1f", power); - else return String.format("%.0f", power); + + final String format; + if (power < .00001) format = "%.8f"; + else if (power < .0001) format = "%.7f"; + else if (power < .001) format = "%.6f"; + else if (power < .01) format = "%.5f"; + else if (power < .1) format = "%.4f"; + else if (power < 1) format = "%.3f"; + else if (power < 10) format = "%.2f"; + else if (power < 100) format = "%.1f"; + else format = "%.0f"; + + // Use English locale because this is never used in UI (only in checkin and dump). + return String.format(Locale.ENGLISH, format, power); } /** diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 60f47d6..e7c58f4 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -2560,14 +2560,24 @@ public final class BatteryStatsImpl extends BatteryStats { addHistoryEventLocked(elapsedRealtime, uptime, code, name, uid); } + boolean ensureStartClockTime(final long currentTime) { + final long ABOUT_ONE_YEAR = 365*24*60*60*1000L; + if (currentTime > ABOUT_ONE_YEAR && mStartClockTime < (currentTime-ABOUT_ONE_YEAR)) { + // If the start clock time has changed by more than a year, then presumably + // the previous time was completely bogus. So we are going to figure out a + // new time based on how much time has elapsed since we started counting. + mStartClockTime = currentTime - (SystemClock.elapsedRealtime()-(mRealtimeStart/1000)); + return true; + } + return false; + } + public void noteCurrentTimeChangedLocked() { final long currentTime = System.currentTimeMillis(); final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); recordCurrentTimeChangeLocked(currentTime, elapsedRealtime, uptime); - if (isStartClockTimeValid()) { - mStartClockTime = currentTime; - } + ensureStartClockTime(currentTime); } public void noteProcessStartLocked(String name, int uid) { @@ -4306,19 +4316,11 @@ public final class BatteryStatsImpl extends BatteryStats { } } - boolean isStartClockTimeValid() { - return mStartClockTime > 365*24*60*60*1000L; - } - @Override public long getStartClockTime() { - if (!isStartClockTimeValid()) { - // If the last clock time we got was very small, then we hadn't had a real - // time yet, so try to get it again. - mStartClockTime = System.currentTimeMillis(); - if (isStartClockTimeValid()) { - recordCurrentTimeChangeLocked(mStartClockTime, SystemClock.elapsedRealtime(), - SystemClock.uptimeMillis()); - } + final long currentTime = System.currentTimeMillis(); + if (ensureStartClockTime(currentTime)) { + recordCurrentTimeChangeLocked(currentTime, SystemClock.elapsedRealtime(), + SystemClock.uptimeMillis()); } return mStartClockTime; } diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java index 444f878..a709bb8 100644 --- a/core/java/com/android/internal/widget/LockPatternView.java +++ b/core/java/com/android/internal/widget/LockPatternView.java @@ -23,10 +23,10 @@ import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Canvas; +import android.graphics.CanvasProperty; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; -import android.graphics.RectF; import android.media.AudioManager; import android.os.Bundle; import android.os.Debug; @@ -38,8 +38,10 @@ import android.provider.Settings; import android.util.AttributeSet; import android.util.IntArray; import android.util.Log; +import android.view.DisplayListCanvas; import android.view.HapticFeedbackConstants; import android.view.MotionEvent; +import android.view.RenderNodeAnimator; import android.view.View; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; @@ -200,10 +202,16 @@ public class LockPatternView extends View { } public static class CellState { - public float scale = 1.0f; - public float translateY = 0.0f; - public float alpha = 1.0f; - public float size; + int row; + int col; + boolean hwAnimating; + CanvasProperty<Float> hwRadius; + CanvasProperty<Float> hwCenterX; + CanvasProperty<Float> hwCenterY; + CanvasProperty<Paint> hwPaint; + float radius; + float translationY; + float alpha = 1f; public float lineEndX = Float.MIN_VALUE; public float lineEndY = Float.MIN_VALUE; public ValueAnimator lineAnimator; @@ -313,7 +321,9 @@ public class LockPatternView extends View { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { mCellStates[i][j] = new CellState(); - mCellStates[i][j].size = mDotSize; + mCellStates[i][j].radius = mDotSize/2; + mCellStates[i][j].row = i; + mCellStates[i][j].col = j; } } @@ -412,6 +422,112 @@ public class LockPatternView extends View { invalidate(); } + public void startCellStateAnimation(CellState cellState, float startAlpha, float endAlpha, + float startTranslationY, float endTranslationY, float startScale, float endScale, + long delay, long duration, + Interpolator interpolator, Runnable finishRunnable) { + if (isHardwareAccelerated()) { + startCellStateAnimationHw(cellState, startAlpha, endAlpha, startTranslationY, + endTranslationY, startScale, endScale, delay, duration, interpolator, + finishRunnable); + } else { + startCellStateAnimationSw(cellState, startAlpha, endAlpha, startTranslationY, + endTranslationY, startScale, endScale, delay, duration, interpolator, + finishRunnable); + } + } + + private void startCellStateAnimationSw(final CellState cellState, + final float startAlpha, final float endAlpha, + final float startTranslationY, final float endTranslationY, + final float startScale, final float endScale, + long delay, long duration, Interpolator interpolator, final Runnable finishRunnable) { + cellState.alpha = startAlpha; + cellState.translationY = startTranslationY; + cellState.radius = mDotSize/2 * startScale; + ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f); + animator.setDuration(duration); + animator.setStartDelay(delay); + animator.setInterpolator(interpolator); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float t = (float) animation.getAnimatedValue(); + cellState.alpha = (1 - t) * startAlpha + t * endAlpha; + cellState.translationY = (1 - t) * startTranslationY + t * endTranslationY; + cellState.radius = mDotSize/2 * ((1 - t) * startScale + t * endScale); + invalidate(); + } + }); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (finishRunnable != null) { + finishRunnable.run(); + } + } + }); + animator.start(); + } + + private void startCellStateAnimationHw(final CellState cellState, + float startAlpha, float endAlpha, + float startTranslationY, float endTranslationY, + float startScale, float endScale, + long delay, long duration, Interpolator interpolator, final Runnable finishRunnable) { + cellState.alpha = endAlpha; + cellState.translationY = endTranslationY; + cellState.radius = mDotSize/2 * endScale; + cellState.hwAnimating = true; + cellState.hwCenterY = CanvasProperty.createFloat( + getCenterYForRow(cellState.row) + startTranslationY); + cellState.hwCenterX = CanvasProperty.createFloat(getCenterXForColumn(cellState.col)); + cellState.hwRadius = CanvasProperty.createFloat(mDotSize/2 * startScale); + mPaint.setColor(getCurrentColor(false)); + mPaint.setAlpha((int) (startAlpha * 255)); + cellState.hwPaint = CanvasProperty.createPaint(new Paint(mPaint)); + + startRtFloatAnimation(cellState.hwCenterY, + getCenterYForRow(cellState.row) + endTranslationY, delay, duration, interpolator); + startRtFloatAnimation(cellState.hwRadius, mDotSize/2 * endScale, delay, duration, + interpolator); + startRtAlphaAnimation(cellState, endAlpha, delay, duration, interpolator, + new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + cellState.hwAnimating = false; + if (finishRunnable != null) { + finishRunnable.run(); + } + } + }); + + invalidate(); + } + + private void startRtAlphaAnimation(CellState cellState, float endAlpha, + long delay, long duration, Interpolator interpolator, + Animator.AnimatorListener listener) { + RenderNodeAnimator animator = new RenderNodeAnimator(cellState.hwPaint, + RenderNodeAnimator.PAINT_ALPHA, (int) (endAlpha * 255)); + animator.setDuration(duration); + animator.setStartDelay(delay); + animator.setInterpolator(interpolator); + animator.setTarget(this); + animator.addListener(listener); + animator.start(); + } + + private void startRtFloatAnimation(CanvasProperty<Float> property, float endValue, + long delay, long duration, Interpolator interpolator) { + RenderNodeAnimator animator = new RenderNodeAnimator(property, endValue); + animator.setDuration(duration); + animator.setStartDelay(delay); + animator.setInterpolator(interpolator); + animator.setTarget(this); + animator.start(); + } + private void notifyCellAdded() { // sendAccessEvent(R.string.lockscreen_access_pattern_cell_added); if (mOnPatternListener != null) { @@ -603,14 +719,15 @@ public class LockPatternView extends View { private void startCellActivatedAnimation(Cell cell) { final CellState cellState = mCellStates[cell.row][cell.column]; - startSizeAnimation(mDotSize, mDotSizeActivated, 96, mLinearOutSlowInInterpolator, + startRadiusAnimation(mDotSize/2, mDotSizeActivated/2, 96, mLinearOutSlowInInterpolator, cellState, new Runnable() { - @Override - public void run() { - startSizeAnimation(mDotSizeActivated, mDotSize, 192, mFastOutSlowInInterpolator, - cellState, null); - } - }); + @Override + public void run() { + startRadiusAnimation(mDotSizeActivated/2, mDotSize/2, 192, + mFastOutSlowInInterpolator, + cellState, null); + } + }); startLineEndAnimation(cellState, mInProgressX, mInProgressY, getCenterXForColumn(cell.column), getCenterYForRow(cell.row)); } @@ -639,13 +756,13 @@ public class LockPatternView extends View { state.lineAnimator = valueAnimator; } - private void startSizeAnimation(float start, float end, long duration, Interpolator interpolator, - final CellState state, final Runnable endRunnable) { + private void startRadiusAnimation(float start, float end, long duration, + Interpolator interpolator, final CellState state, final Runnable endRunnable) { ValueAnimator valueAnimator = ValueAnimator.ofFloat(start, end); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { - state.size = (float) animation.getAnimatedValue(); + state.radius = (float) animation.getAnimatedValue(); invalidate(); } }); @@ -969,10 +1086,16 @@ public class LockPatternView extends View { for (int j = 0; j < 3; j++) { CellState cellState = mCellStates[i][j]; float centerX = getCenterXForColumn(j); - float size = cellState.size * cellState.scale; - float translationY = cellState.translateY; - drawCircle(canvas, (int) centerX, (int) centerY + translationY, - size, drawLookup[i][j], cellState.alpha); + float translationY = cellState.translationY; + if (isHardwareAccelerated() && cellState.hwAnimating) { + DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas; + displayListCanvas.drawCircle(cellState.hwCenterX, cellState.hwCenterY, + cellState.hwRadius, cellState.hwPaint); + } else { + drawCircle(canvas, (int) centerX, (int) centerY + translationY, + cellState.radius, drawLookup[i][j], cellState.alpha); + + } } } @@ -1055,11 +1178,11 @@ public class LockPatternView extends View { /** * @param partOfPattern Whether this circle is part of the pattern. */ - private void drawCircle(Canvas canvas, float centerX, float centerY, float size, + private void drawCircle(Canvas canvas, float centerX, float centerY, float radius, boolean partOfPattern, float alpha) { mPaint.setColor(getCurrentColor(partOfPattern)); mPaint.setAlpha((int) (alpha * 255)); - canvas.drawCircle(centerX, centerY, size/2, mPaint); + canvas.drawCircle(centerX, centerY, radius, mPaint); } @Override @@ -1290,7 +1413,6 @@ public class LockPatternView extends View { float centerY = getCenterYForRow(row); float cellheight = mSquareHeight * mHitFactor * 0.5f; float cellwidth = mSquareWidth * mHitFactor * 0.5f; - float translationY = cell.translateY; bounds.left = (int) (centerX - cellwidth); bounds.right = (int) (centerX + cellwidth); bounds.top = (int) (centerY - cellheight); diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index bffbab7..bae2cde 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -590,6 +590,7 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) char cpuAbiListBuf[sizeof("--cpu-abilist=") + PROPERTY_VALUE_MAX]; char methodTraceFileBuf[sizeof("-Xmethod-trace-file:") + PROPERTY_VALUE_MAX]; char methodTraceFileSizeBuf[sizeof("-Xmethod-trace-file-size:") + PROPERTY_VALUE_MAX]; + char fingerprintBuf[sizeof("-Xfingerprint:") + PROPERTY_VALUE_MAX]; bool checkJni = false; property_get("dalvik.vm.checkjni", propBuf, ""); @@ -908,6 +909,12 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) addOption("--generate-debug-info"); } + /* + * Retrieve the build fingerprint and provide it to the runtime. That way, ANR dumps will + * contain the fingerprint and can be parsed. + */ + parseRuntimeOption("ro.build.fingerprint", fingerprintBuf, "-Xfingerprint:"); + initArgs.version = JNI_VERSION_1_4; initArgs.options = mOptions.editArray(); initArgs.nOptions = mOptions.size(); diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 0d80a7f..670d3c0 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -475,6 +475,14 @@ static void FromColor_D4444_Raw(void* dst, const SkColor src[], int width, } } +static void FromColor_DA8(void* dst, const SkColor src[], int width, int x, int y) { + uint8_t* d = (uint8_t*)dst; + + for (int stop = x + width; x < stop; x++) { + *d++ = SkColorGetA(*src++); + } +} + // can return NULL static FromColorProc ChooseFromColorProc(const SkBitmap& bitmap) { switch (bitmap.colorType()) { @@ -485,6 +493,8 @@ static FromColorProc ChooseFromColorProc(const SkBitmap& bitmap) { FromColor_D4444_Raw; case kRGB_565_SkColorType: return FromColor_D565; + case kAlpha_8_SkColorType: + return FromColor_DA8; default: break; } @@ -632,6 +642,15 @@ static void ToColor_SI8_Opaque(SkColor dst[], const void* src, int width, } while (--width != 0); } +static void ToColor_SA8(SkColor dst[], const void* src, int width, SkColorTable*) { + SkASSERT(width > 0); + const uint8_t* s = (const uint8_t*)src; + do { + uint8_t c = *s++; + *dst++ = SkColorSetARGB(c, c, c, c); + } while (--width != 0); +} + // can return NULL static ToColorProc ChooseToColorProc(const SkBitmap& src) { switch (src.colorType()) { @@ -673,6 +692,8 @@ static ToColorProc ChooseToColorProc(const SkBitmap& src) { default: return NULL; } + case kAlpha_8_SkColorType: + return ToColor_SA8; default: break; } diff --git a/core/jni/android_hardware_camera2_DngCreator.cpp b/core/jni/android_hardware_camera2_DngCreator.cpp index 995d39f..ba08237 100644 --- a/core/jni/android_hardware_camera2_DngCreator.cpp +++ b/core/jni/android_hardware_camera2_DngCreator.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -//#define LOG_NDEBUG 0 +#define LOG_NDEBUG 0 #define LOG_TAG "DngCreator_JNI" #include <inttypes.h> #include <string.h> @@ -26,6 +26,7 @@ #include <utils/StrongPointer.h> #include <utils/RefBase.h> #include <utils/Vector.h> +#include <utils/String8.h> #include <cutils/properties.h> #include <system/camera_metadata.h> #include <camera/CameraMetadata.h> @@ -48,13 +49,22 @@ using namespace android; using namespace img_utils; -#define BAIL_IF_INVALID(expr, jnienv, tagId, writer) \ +#define BAIL_IF_INVALID_RET_BOOL(expr, jnienv, tagId, writer) \ if ((expr) != OK) { \ jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \ "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \ - return; \ + return false; \ } + +#define BAIL_IF_INVALID_RET_NULL_SP(expr, jnienv, tagId, writer) \ + if ((expr) != OK) { \ + jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \ + "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \ + return nullptr; \ + } + + #define BAIL_IF_INVALID_R(expr, jnienv, tagId, writer) \ if ((expr) != OK) { \ jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \ @@ -62,14 +72,14 @@ using namespace img_utils; return -1; \ } - -#define BAIL_IF_EMPTY(entry, jnienv, tagId, writer) \ +#define BAIL_IF_EMPTY_RET_NULL_SP(entry, jnienv, tagId, writer) \ if (entry.count == 0) { \ jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \ "Missing metadata fields for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \ - return; \ + return nullptr; \ } + #define ANDROID_DNGCREATOR_CTX_JNI_ID "mNativeContext" static struct { @@ -102,6 +112,26 @@ enum { TIFF_IFD_GPSINFO = 2, }; + +/** + * POD container class for GPS tag data. + */ +class GpsData { +public: + enum { + GPS_VALUE_LENGTH = 6, + GPS_REF_LENGTH = 2, + GPS_DATE_LENGTH = 11, + }; + + uint32_t mLatitude[GPS_VALUE_LENGTH]; + uint32_t mLongitude[GPS_VALUE_LENGTH]; + uint32_t mTimestamp[GPS_VALUE_LENGTH]; + uint8_t mLatitudeRef[GPS_REF_LENGTH]; + uint8_t mLongitudeRef[GPS_REF_LENGTH]; + uint8_t mDate[GPS_DATE_LENGTH]; +}; + // ---------------------------------------------------------------------------- /** @@ -109,8 +139,11 @@ enum { */ class NativeContext : public LightRefBase<NativeContext> { - public: + enum { + DATETIME_COUNT = 20, + }; + NativeContext(const CameraMetadata& characteristics, const CameraMetadata& result); virtual ~NativeContext(); @@ -119,12 +152,28 @@ public: std::shared_ptr<const CameraMetadata> getCharacteristics() const; std::shared_ptr<const CameraMetadata> getResult() const; - uint32_t getThumbnailWidth(); - uint32_t getThumbnailHeight(); - const uint8_t* getThumbnail(); + uint32_t getThumbnailWidth() const; + uint32_t getThumbnailHeight() const; + const uint8_t* getThumbnail() const; + bool hasThumbnail() const; bool setThumbnail(const uint8_t* buffer, uint32_t width, uint32_t height); + void setOrientation(uint16_t orientation); + uint16_t getOrientation() const; + + void setDescription(const String8& desc); + String8 getDescription() const; + bool hasDescription() const; + + void setGpsData(const GpsData& data); + GpsData getGpsData() const; + bool hasGpsData() const; + + void setCaptureTime(const String8& formattedCaptureTime); + String8 getCaptureTime() const; + bool hasCaptureTime() const; + private: Vector<uint8_t> mCurrentThumbnail; TiffWriter mWriter; @@ -132,12 +181,21 @@ private: std::shared_ptr<CameraMetadata> mResult; uint32_t mThumbnailWidth; uint32_t mThumbnailHeight; + uint16_t mOrientation; + bool mThumbnailSet; + bool mGpsSet; + bool mDescriptionSet; + bool mCaptureTimeSet; + String8 mDescription; + GpsData mGpsData; + String8 mFormattedCaptureTime; }; NativeContext::NativeContext(const CameraMetadata& characteristics, const CameraMetadata& result) : mCharacteristics(std::make_shared<CameraMetadata>(characteristics)), mResult(std::make_shared<CameraMetadata>(result)), mThumbnailWidth(0), - mThumbnailHeight(0) {} + mThumbnailHeight(0), mOrientation(0), mThumbnailSet(false), mGpsSet(false), + mDescriptionSet(false), mCaptureTimeSet(false) {} NativeContext::~NativeContext() {} @@ -153,18 +211,22 @@ std::shared_ptr<const CameraMetadata> NativeContext::getResult() const { return mResult; } -uint32_t NativeContext::getThumbnailWidth() { +uint32_t NativeContext::getThumbnailWidth() const { return mThumbnailWidth; } -uint32_t NativeContext::getThumbnailHeight() { +uint32_t NativeContext::getThumbnailHeight() const { return mThumbnailHeight; } -const uint8_t* NativeContext::getThumbnail() { +const uint8_t* NativeContext::getThumbnail() const { return mCurrentThumbnail.array(); } +bool NativeContext::hasThumbnail() const { + return mThumbnailSet; +} + bool NativeContext::setThumbnail(const uint8_t* buffer, uint32_t width, uint32_t height) { mThumbnailWidth = width; mThumbnailHeight = height; @@ -177,9 +239,57 @@ bool NativeContext::setThumbnail(const uint8_t* buffer, uint32_t width, uint32_t uint8_t* thumb = mCurrentThumbnail.editArray(); memcpy(thumb, buffer, size); + mThumbnailSet = true; return true; } +void NativeContext::setOrientation(uint16_t orientation) { + mOrientation = orientation; +} + +uint16_t NativeContext::getOrientation() const { + return mOrientation; +} + +void NativeContext::setDescription(const String8& desc) { + mDescription = desc; + mDescriptionSet = true; +} + +String8 NativeContext::getDescription() const { + return mDescription; +} + +bool NativeContext::hasDescription() const { + return mDescriptionSet; +} + +void NativeContext::setGpsData(const GpsData& data) { + mGpsData = data; + mGpsSet = true; +} + +GpsData NativeContext::getGpsData() const { + return mGpsData; +} + +bool NativeContext::hasGpsData() const { + return mGpsSet; +} + +void NativeContext::setCaptureTime(const String8& formattedCaptureTime) { + mFormattedCaptureTime = formattedCaptureTime; + mCaptureTimeSet = true; +} + +String8 NativeContext::getCaptureTime() const { + return mFormattedCaptureTime; +} + +bool NativeContext::hasCaptureTime() const { + return mCaptureTimeSet; +} + // End of NativeContext // ---------------------------------------------------------------------------- @@ -211,7 +321,7 @@ private: JniOutputStream::JniOutputStream(JNIEnv* env, jobject outStream) : mOutputStream(outStream), mEnv(env) { mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH); - if (mByteArray == NULL) { + if (mByteArray == nullptr) { jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array."); } } @@ -286,7 +396,7 @@ private: JniInputStream::JniInputStream(JNIEnv* env, jobject inStream) : mInStream(inStream), mEnv(env) { mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH); - if (mByteArray == NULL) { + if (mByteArray == nullptr) { jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array."); } } @@ -372,7 +482,7 @@ private: JniInputByteBuffer::JniInputByteBuffer(JNIEnv* env, jobject inBuf) : mInBuf(inBuf), mEnv(env) { mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH); - if (mByteArray == NULL) { + if (mByteArray == nullptr) { jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array."); } } @@ -600,6 +710,7 @@ status_t DirectStripSource::writeToStream(Output& stream, uint32_t count) { return BAD_VALUE; } + if (mPixStride == mBytesPerSample * mSamplesPerPixel && mRowStride == mWidth * mBytesPerSample * mSamplesPerPixel) { ALOGV("%s: Using direct single-pass write for strip.", __FUNCTION__); @@ -643,37 +754,48 @@ uint32_t DirectStripSource::getIfd() const { // ---------------------------------------------------------------------------- /** - * Given a buffer crop rectangle relative to the pixel array size, and the active array crop - * rectangle for the camera characteristics, set the default crop rectangle in the TiffWriter - * relative to the buffer crop rectangle origin. + * Given a buffer crop rectangle relative to the pixel array size, and the pre-correction active + * array crop rectangle for the camera characteristics, set the default crop rectangle in the + * TiffWriter relative to the buffer crop rectangle origin. */ static status_t calculateAndSetCrop(JNIEnv* env, const CameraMetadata& characteristics, - uint32_t bufXMin, uint32_t bufYMin, uint32_t bufWidth, uint32_t bufHeight, - TiffWriter* writer) { + uint32_t bufWidth, uint32_t bufHeight, sp<TiffWriter> writer) { camera_metadata_ro_entry entry = - characteristics.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE); + characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE); uint32_t xmin = static_cast<uint32_t>(entry.data.i32[0]); uint32_t ymin = static_cast<uint32_t>(entry.data.i32[1]); uint32_t width = static_cast<uint32_t>(entry.data.i32[2]); uint32_t height = static_cast<uint32_t>(entry.data.i32[3]); + const uint32_t margin = 8; // Default margin recommended by Adobe for interpolation. + + // Crop based on pre-correction array for pixel array uint32_t aLeft = xmin; uint32_t aTop = ymin; uint32_t aRight = xmin + width; uint32_t aBottom = ymin + height; - const uint32_t margin = 8; // Default margin recommended by Adobe for interpolation. - - uint32_t bLeft = bufXMin + margin; - uint32_t bTop = bufYMin + margin; - uint32_t bRight = bufXMin + bufWidth - margin; - uint32_t bBottom = bufYMin + bufHeight - margin; + // 8 pixel border crop for pixel array dimens + uint32_t bLeft = margin; + uint32_t bTop = margin; + uint32_t bRight = bufWidth - margin; + uint32_t bBottom = bufHeight - margin; + // Set the crop to be the intersection of the two rectangles uint32_t defaultCropOrigin[] = {std::max(aLeft, bLeft), std::max(aTop, bTop)}; uint32_t defaultCropSize[] = {std::min(aRight, bRight) - defaultCropOrigin[0], std::min(aBottom, bBottom) - defaultCropOrigin[1]}; + // If using buffers with pre-correction array dimens, switch to 8 pixel border crop + // relative to the pixel array dimens + if (bufWidth == width && bufHeight == height) { + defaultCropOrigin[0] = xmin + margin; + defaultCropOrigin[1] = ymin + margin; + defaultCropSize[0] = width - margin; + defaultCropSize[1] = height - margin; + } + BAIL_IF_INVALID_R(writer->addEntry(TAG_DEFAULTCROPORIGIN, 2, defaultCropOrigin, TIFF_IFD_0), env, TAG_DEFAULTCROPORIGIN, writer); BAIL_IF_INVALID_R(writer->addEntry(TAG_DEFAULTCROPSIZE, 2, defaultCropSize, @@ -682,9 +804,8 @@ static status_t calculateAndSetCrop(JNIEnv* env, const CameraMetadata& character return OK; } -static bool validateDngHeader(JNIEnv* env, TiffWriter* writer, +static bool validateDngHeader(JNIEnv* env, sp<TiffWriter> writer, const CameraMetadata& characteristics, jint width, jint height) { - // TODO: handle lens shading map, etc. conversions for other raw buffer sizes. if (width <= 0) { jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \ "Image width %d is invalid", width); @@ -710,20 +831,7 @@ static bool validateDngHeader(JNIEnv* env, TiffWriter* writer, bool matchesPixelArray = (pWidth == width && pHeight == height); bool matchesPreCorrectionArray = (cWidth == width && cHeight == height); - if (matchesPixelArray) { - if (calculateAndSetCrop(env, characteristics, 0, 0, static_cast<uint32_t>(pWidth), - static_cast<uint32_t>(pHeight), writer) != OK) { - return false; - } - } else if (matchesPreCorrectionArray) { - if (calculateAndSetCrop(env, characteristics, - static_cast<uint32_t>(preCorrectionEntry.data.i32[0]), - static_cast<uint32_t>(preCorrectionEntry.data.i32[1]), - static_cast<uint32_t>(preCorrectionEntry.data.i32[2]), - static_cast<uint32_t>(preCorrectionEntry.data.i32[3]), writer) != OK) { - return false; - } - } else { + if (!(matchesPixelArray || matchesPreCorrectionArray)) { jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \ "Image dimensions (w=%d,h=%d) are invalid, must match either the pixel " "array size (w=%d, h=%d) or the pre-correction array size (w=%d, h=%d)", @@ -734,12 +842,12 @@ static bool validateDngHeader(JNIEnv* env, TiffWriter* writer, return true; } -static status_t moveEntries(TiffWriter* writer, uint32_t ifdFrom, uint32_t ifdTo, +static status_t moveEntries(sp<TiffWriter> writer, uint32_t ifdFrom, uint32_t ifdTo, const Vector<uint16_t>& entries) { for (size_t i = 0; i < entries.size(); ++i) { uint16_t tagId = entries[i]; sp<TiffEntry> entry = writer->getEntry(tagId, ifdFrom); - if (entry == NULL) { + if (entry.get() == nullptr) { ALOGE("%s: moveEntries failed, entry %u not found in IFD %u", __FUNCTION__, tagId, ifdFrom); return BAD_VALUE; @@ -881,7 +989,7 @@ static void DngCreator_setNativeContext(JNIEnv* env, jobject thiz, sp<NativeCont ALOGV("%s:", __FUNCTION__); NativeContext* current = DngCreator_getNativeContext(env, thiz); - if (context != NULL) { + if (context != nullptr) { context->incStrong((void*) DngCreator_setNativeContext); } @@ -893,15 +1001,6 @@ static void DngCreator_setNativeContext(JNIEnv* env, jobject thiz, sp<NativeCont reinterpret_cast<jlong>(context.get())); } -static TiffWriter* DngCreator_getCreator(JNIEnv* env, jobject thiz) { - ALOGV("%s:", __FUNCTION__); - NativeContext* current = DngCreator_getNativeContext(env, thiz); - if (current) { - return current->getWriter(); - } - return NULL; -} - static void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) { ALOGV("%s:", __FUNCTION__); @@ -938,7 +1037,62 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt } sp<NativeContext> nativeContext = new NativeContext(characteristics, results); - TiffWriter* writer = nativeContext->getWriter(); + + const char* captureTime = env->GetStringUTFChars(formattedCaptureTime, nullptr); + + size_t len = strlen(captureTime) + 1; + if (len != NativeContext::DATETIME_COUNT) { + jniThrowException(env, "java/lang/IllegalArgumentException", + "Formatted capture time string length is not required 20 characters"); + return; + } + + nativeContext->setCaptureTime(String8(captureTime)); + + DngCreator_setNativeContext(env, thiz, nativeContext); +} + +static sp<TiffWriter> DngCreator_setup(JNIEnv* env, jobject thiz, uint32_t imageWidth, + uint32_t imageHeight) { + + NativeContext* nativeContext = DngCreator_getNativeContext(env, thiz); + + if (nativeContext == nullptr) { + jniThrowException(env, "java/lang/AssertionError", + "No native context, must call init before other operations."); + return nullptr; + } + + CameraMetadata characteristics = *(nativeContext->getCharacteristics()); + CameraMetadata results = *(nativeContext->getResult()); + + sp<TiffWriter> writer = new TiffWriter(); + + uint32_t preWidth = 0; + uint32_t preHeight = 0; + { + // Check dimensions + camera_metadata_entry entry = + characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE); + BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_IMAGEWIDTH, writer); + preWidth = static_cast<uint32_t>(entry.data.i32[2]); + preHeight = static_cast<uint32_t>(entry.data.i32[3]); + + camera_metadata_entry pixelArrayEntry = + characteristics.find(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE); + uint32_t pixWidth = static_cast<uint32_t>(pixelArrayEntry.data.i32[0]); + uint32_t pixHeight = static_cast<uint32_t>(pixelArrayEntry.data.i32[1]); + + if (!((imageWidth == preWidth && imageHeight == preHeight) || + (imageWidth == pixWidth && imageHeight == pixHeight))) { + jniThrowException(env, "java/lang/AssertionError", + "Height and width of imate buffer did not match height and width of" + "either the preCorrectionActiveArraySize or the pixelArraySize."); + return nullptr; + } + } + + writer->addIfd(TIFF_IFD_0); @@ -946,8 +1100,6 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt const uint32_t samplesPerPixel = 1; const uint32_t bitsPerSample = BITS_PER_SAMPLE; - uint32_t imageWidth = 0; - uint32_t imageHeight = 0; OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB; uint8_t cfaPlaneColor[3] = {0, 1, 2}; @@ -961,93 +1113,86 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt { // Set orientation uint16_t orientation = 1; // Normal - BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), env, - TAG_ORIENTATION, writer); + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), + env, TAG_ORIENTATION, writer); } { // Set subfiletype uint32_t subfileType = 0; // Main image - BAIL_IF_INVALID(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, TIFF_IFD_0), env, - TAG_NEWSUBFILETYPE, writer); + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, + TIFF_IFD_0), env, TAG_NEWSUBFILETYPE, writer); } { // Set bits per sample uint16_t bits = static_cast<uint16_t>(bitsPerSample); - BAIL_IF_INVALID(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env, + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env, TAG_BITSPERSAMPLE, writer); } { // Set compression uint16_t compression = 1; // None - BAIL_IF_INVALID(writer->addEntry(TAG_COMPRESSION, 1, &compression, TIFF_IFD_0), env, - TAG_COMPRESSION, writer); + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COMPRESSION, 1, &compression, + TIFF_IFD_0), env, TAG_COMPRESSION, writer); } { // Set dimensions - camera_metadata_entry entry = - characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE); - BAIL_IF_EMPTY(entry, env, TAG_IMAGEWIDTH, writer); - uint32_t width = static_cast<uint32_t>(entry.data.i32[2]); - uint32_t height = static_cast<uint32_t>(entry.data.i32[3]); - BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEWIDTH, 1, &width, TIFF_IFD_0), env, - TAG_IMAGEWIDTH, writer); - BAIL_IF_INVALID(writer->addEntry(TAG_IMAGELENGTH, 1, &height, TIFF_IFD_0), env, - TAG_IMAGELENGTH, writer); - imageWidth = width; - imageHeight = height; + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEWIDTH, 1, &imageWidth, TIFF_IFD_0), + env, TAG_IMAGEWIDTH, writer); + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGELENGTH, 1, &imageHeight, TIFF_IFD_0), + env, TAG_IMAGELENGTH, writer); } { // Set photometric interpretation uint16_t interpretation = 32803; // CFA - BAIL_IF_INVALID(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, &interpretation, - TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer); + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, + &interpretation, TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer); } { // Set blacklevel tags camera_metadata_entry entry = characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN); - BAIL_IF_EMPTY(entry, env, TAG_BLACKLEVEL, writer); + BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_BLACKLEVEL, writer); const uint32_t* blackLevel = reinterpret_cast<const uint32_t*>(entry.data.i32); - BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVEL, entry.count, blackLevel, TIFF_IFD_0), env, - TAG_BLACKLEVEL, writer); + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVEL, entry.count, blackLevel, + TIFF_IFD_0), env, TAG_BLACKLEVEL, writer); uint16_t repeatDim[2] = {2, 2}; - BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim, TIFF_IFD_0), env, - TAG_BLACKLEVELREPEATDIM, writer); + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim, + TIFF_IFD_0), env, TAG_BLACKLEVELREPEATDIM, writer); } { // Set samples per pixel uint16_t samples = static_cast<uint16_t>(samplesPerPixel); - BAIL_IF_INVALID(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0), + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0), env, TAG_SAMPLESPERPIXEL, writer); } { // Set planar configuration uint16_t config = 1; // Chunky - BAIL_IF_INVALID(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, TIFF_IFD_0), - env, TAG_PLANARCONFIGURATION, writer); + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, + TIFF_IFD_0), env, TAG_PLANARCONFIGURATION, writer); } { // Set CFA pattern dimensions uint16_t repeatDim[2] = {2, 2}; - BAIL_IF_INVALID(writer->addEntry(TAG_CFAREPEATPATTERNDIM, 2, repeatDim, TIFF_IFD_0), - env, TAG_CFAREPEATPATTERNDIM, writer); + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAREPEATPATTERNDIM, 2, repeatDim, + TIFF_IFD_0), env, TAG_CFAREPEATPATTERNDIM, writer); } { // Set CFA pattern camera_metadata_entry entry = characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT); - BAIL_IF_EMPTY(entry, env, TAG_CFAPATTERN, writer); + BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_CFAPATTERN, writer); const int cfaLength = 4; cfaEnum = entry.data.u8[0]; @@ -1057,30 +1202,30 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt "Invalid metadata for tag %d", TAG_CFAPATTERN); } - BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, cfaLength, cfa, TIFF_IFD_0), env, - TAG_CFAPATTERN, writer); + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAPATTERN, cfaLength, cfa, TIFF_IFD_0), + env, TAG_CFAPATTERN, writer); opcodeCfaLayout = convertCFAEnumToOpcodeLayout(cfaEnum); } { // Set CFA plane color - BAIL_IF_INVALID(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor, TIFF_IFD_0), - env, TAG_CFAPLANECOLOR, writer); + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor, + TIFF_IFD_0), env, TAG_CFAPLANECOLOR, writer); } { // Set CFA layout uint16_t cfaLayout = 1; - BAIL_IF_INVALID(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0), + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0), env, TAG_CFALAYOUT, writer); } { // image description uint8_t imageDescription = '\0'; // empty - BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEDESCRIPTION, 1, &imageDescription, TIFF_IFD_0), - env, TAG_IMAGEDESCRIPTION, writer); + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEDESCRIPTION, 1, &imageDescription, + TIFF_IFD_0), env, TAG_IMAGEDESCRIPTION, writer); } { @@ -1091,8 +1236,8 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt property_get("ro.product.manufacturer", manufacturer, ""); uint32_t count = static_cast<uint32_t>(strlen(manufacturer)) + 1; - BAIL_IF_INVALID(writer->addEntry(TAG_MAKE, count, reinterpret_cast<uint8_t*>(manufacturer), - TIFF_IFD_0), env, TAG_MAKE, writer); + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_MAKE, count, + reinterpret_cast<uint8_t*>(manufacturer), TIFF_IFD_0), env, TAG_MAKE, writer); } { @@ -1103,23 +1248,23 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt property_get("ro.product.model", model, ""); uint32_t count = static_cast<uint32_t>(strlen(model)) + 1; - BAIL_IF_INVALID(writer->addEntry(TAG_MODEL, count, reinterpret_cast<uint8_t*>(model), - TIFF_IFD_0), env, TAG_MODEL, writer); + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_MODEL, count, + reinterpret_cast<uint8_t*>(model), TIFF_IFD_0), env, TAG_MODEL, writer); } { // x resolution uint32_t xres[] = { 72, 1 }; // default 72 ppi - BAIL_IF_INVALID(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0), + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0), env, TAG_XRESOLUTION, writer); // y resolution uint32_t yres[] = { 72, 1 }; // default 72 ppi - BAIL_IF_INVALID(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0), + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0), env, TAG_YRESOLUTION, writer); uint16_t unit = 2; // inches - BAIL_IF_INVALID(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0), + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0), env, TAG_RESOLUTIONUNIT, writer); } @@ -1128,52 +1273,41 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt char software[PROPERTY_VALUE_MAX]; property_get("ro.build.fingerprint", software, ""); uint32_t count = static_cast<uint32_t>(strlen(software)) + 1; - BAIL_IF_INVALID(writer->addEntry(TAG_SOFTWARE, count, reinterpret_cast<uint8_t*>(software), - TIFF_IFD_0), env, TAG_SOFTWARE, writer); + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SOFTWARE, count, + reinterpret_cast<uint8_t*>(software), TIFF_IFD_0), env, TAG_SOFTWARE, writer); } - { + if (nativeContext->hasCaptureTime()) { // datetime - const size_t DATETIME_COUNT = 20; - const char* captureTime = env->GetStringUTFChars(formattedCaptureTime, NULL); - - size_t len = strlen(captureTime) + 1; - if (len != DATETIME_COUNT) { - jniThrowException(env, "java/lang/IllegalArgumentException", - "Timestamp string length is not required 20 characters"); - return; - } + String8 captureTime = nativeContext->getCaptureTime(); - if (writer->addEntry(TAG_DATETIME, DATETIME_COUNT, - reinterpret_cast<const uint8_t*>(captureTime), TIFF_IFD_0) != OK) { - env->ReleaseStringUTFChars(formattedCaptureTime, captureTime); + if (writer->addEntry(TAG_DATETIME, NativeContext::DATETIME_COUNT, + reinterpret_cast<const uint8_t*>(captureTime.string()), TIFF_IFD_0) != OK) { jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "Invalid metadata for tag %x", TAG_DATETIME); - return; + return nullptr; } // datetime original - if (writer->addEntry(TAG_DATETIMEORIGINAL, DATETIME_COUNT, - reinterpret_cast<const uint8_t*>(captureTime), TIFF_IFD_0) != OK) { - env->ReleaseStringUTFChars(formattedCaptureTime, captureTime); + if (writer->addEntry(TAG_DATETIMEORIGINAL, NativeContext::DATETIME_COUNT, + reinterpret_cast<const uint8_t*>(captureTime.string()), TIFF_IFD_0) != OK) { jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "Invalid metadata for tag %x", TAG_DATETIMEORIGINAL); - return; + return nullptr; } - env->ReleaseStringUTFChars(formattedCaptureTime, captureTime); } { // TIFF/EP standard id uint8_t standardId[] = { 1, 0, 0, 0 }; - BAIL_IF_INVALID(writer->addEntry(TAG_TIFFEPSTANDARDID, 4, standardId, + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_TIFFEPSTANDARDID, 4, standardId, TIFF_IFD_0), env, TAG_TIFFEPSTANDARDID, writer); } { // copyright uint8_t copyright = '\0'; // empty - BAIL_IF_INVALID(writer->addEntry(TAG_COPYRIGHT, 1, ©right, + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COPYRIGHT, 1, ©right, TIFF_IFD_0), env, TAG_COPYRIGHT, writer); } @@ -1181,7 +1315,7 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt // exposure time camera_metadata_entry entry = results.find(ANDROID_SENSOR_EXPOSURE_TIME); - BAIL_IF_EMPTY(entry, env, TAG_EXPOSURETIME, writer); + BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_EXPOSURETIME, writer); int64_t exposureTime = *(entry.data.i64); @@ -1189,7 +1323,7 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt // Should be unreachable jniThrowException(env, "java/lang/IllegalArgumentException", "Negative exposure time in metadata"); - return; + return nullptr; } // Ensure exposure time doesn't overflow (for exposures > 4s) @@ -1201,12 +1335,12 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt // Should be unreachable jniThrowException(env, "java/lang/IllegalArgumentException", "Exposure time too long"); - return; + return nullptr; } } uint32_t exposure[] = { static_cast<uint32_t>(exposureTime), denominator }; - BAIL_IF_INVALID(writer->addEntry(TAG_EXPOSURETIME, 1, exposure, + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_EXPOSURETIME, 1, exposure, TIFF_IFD_0), env, TAG_EXPOSURETIME, writer); } @@ -1215,13 +1349,13 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt // ISO speed ratings camera_metadata_entry entry = results.find(ANDROID_SENSOR_SENSITIVITY); - BAIL_IF_EMPTY(entry, env, TAG_ISOSPEEDRATINGS, writer); + BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ISOSPEEDRATINGS, writer); int32_t tempIso = *(entry.data.i32); if (tempIso < 0) { jniThrowException(env, "java/lang/IllegalArgumentException", "Negative ISO value"); - return; + return nullptr; } if (tempIso > UINT16_MAX) { @@ -1230,7 +1364,7 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt } uint16_t iso = static_cast<uint16_t>(tempIso); - BAIL_IF_INVALID(writer->addEntry(TAG_ISOSPEEDRATINGS, 1, &iso, + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ISOSPEEDRATINGS, 1, &iso, TIFF_IFD_0), env, TAG_ISOSPEEDRATINGS, writer); } @@ -1238,10 +1372,10 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt // focal length camera_metadata_entry entry = results.find(ANDROID_LENS_FOCAL_LENGTH); - BAIL_IF_EMPTY(entry, env, TAG_FOCALLENGTH, writer); + BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_FOCALLENGTH, writer); uint32_t focalLength[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 }; - BAIL_IF_INVALID(writer->addEntry(TAG_FOCALLENGTH, 1, focalLength, + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FOCALLENGTH, 1, focalLength, TIFF_IFD_0), env, TAG_FOCALLENGTH, writer); } @@ -1249,39 +1383,39 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt // f number camera_metadata_entry entry = results.find(ANDROID_LENS_APERTURE); - BAIL_IF_EMPTY(entry, env, TAG_FNUMBER, writer); + BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_FNUMBER, writer); uint32_t fnum[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 }; - BAIL_IF_INVALID(writer->addEntry(TAG_FNUMBER, 1, fnum, + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FNUMBER, 1, fnum, TIFF_IFD_0), env, TAG_FNUMBER, writer); } { // Set DNG version information uint8_t version[4] = {1, 4, 0, 0}; - BAIL_IF_INVALID(writer->addEntry(TAG_DNGVERSION, 4, version, TIFF_IFD_0), + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DNGVERSION, 4, version, TIFF_IFD_0), env, TAG_DNGVERSION, writer); uint8_t backwardVersion[4] = {1, 1, 0, 0}; - BAIL_IF_INVALID(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion, TIFF_IFD_0), - env, TAG_DNGBACKWARDVERSION, writer); + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion, + TIFF_IFD_0), env, TAG_DNGBACKWARDVERSION, writer); } { // Set whitelevel camera_metadata_entry entry = characteristics.find(ANDROID_SENSOR_INFO_WHITE_LEVEL); - BAIL_IF_EMPTY(entry, env, TAG_WHITELEVEL, writer); + BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_WHITELEVEL, writer); uint32_t whiteLevel = static_cast<uint32_t>(entry.data.i32[0]); - BAIL_IF_INVALID(writer->addEntry(TAG_WHITELEVEL, 1, &whiteLevel, TIFF_IFD_0), env, - TAG_WHITELEVEL, writer); + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_WHITELEVEL, 1, &whiteLevel, TIFF_IFD_0), + env, TAG_WHITELEVEL, writer); } { // Set default scale uint32_t defaultScale[4] = {1, 1, 1, 1}; - BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTSCALE, 2, defaultScale, TIFF_IFD_0), - env, TAG_DEFAULTSCALE, writer); + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DEFAULTSCALE, 2, defaultScale, + TIFF_IFD_0), env, TAG_DEFAULTSCALE, writer); } bool singleIlluminant = false; @@ -1289,7 +1423,7 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt // Set calibration illuminants camera_metadata_entry entry1 = characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1); - BAIL_IF_EMPTY(entry1, env, TAG_CALIBRATIONILLUMINANT1, writer); + BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_CALIBRATIONILLUMINANT1, writer); camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT2); if (entry2.count == 0) { @@ -1297,12 +1431,12 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt } uint16_t ref1 = entry1.data.u8[0]; - BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT1, 1, &ref1, + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CALIBRATIONILLUMINANT1, 1, &ref1, TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1, writer); if (!singleIlluminant) { uint16_t ref2 = entry2.data.u8[0]; - BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2, + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2, TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2, writer); } } @@ -1311,7 +1445,7 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt // Set color transforms camera_metadata_entry entry1 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1); - BAIL_IF_EMPTY(entry1, env, TAG_COLORMATRIX1, writer); + BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_COLORMATRIX1, writer); int32_t colorTransform1[entry1.count * 2]; @@ -1321,12 +1455,12 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt colorTransform1[ctr++] = entry1.data.r[i].denominator; } - BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX1, entry1.count, colorTransform1, - TIFF_IFD_0), env, TAG_COLORMATRIX1, writer); + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COLORMATRIX1, entry1.count, + colorTransform1, TIFF_IFD_0), env, TAG_COLORMATRIX1, writer); if (!singleIlluminant) { camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM2); - BAIL_IF_EMPTY(entry2, env, TAG_COLORMATRIX2, writer); + BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_COLORMATRIX2, writer); int32_t colorTransform2[entry2.count * 2]; ctr = 0; @@ -1335,8 +1469,8 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt colorTransform2[ctr++] = entry2.data.r[i].denominator; } - BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX2, entry2.count, colorTransform2, - TIFF_IFD_0), env, TAG_COLORMATRIX2, writer); + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COLORMATRIX2, entry2.count, + colorTransform2, TIFF_IFD_0), env, TAG_COLORMATRIX2, writer); } } @@ -1344,7 +1478,7 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt // Set calibration transforms camera_metadata_entry entry1 = characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1); - BAIL_IF_EMPTY(entry1, env, TAG_CAMERACALIBRATION1, writer); + BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_CAMERACALIBRATION1, writer); int32_t calibrationTransform1[entry1.count * 2]; @@ -1354,13 +1488,13 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt calibrationTransform1[ctr++] = entry1.data.r[i].denominator; } - BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION1, entry1.count, + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CAMERACALIBRATION1, entry1.count, calibrationTransform1, TIFF_IFD_0), env, TAG_CAMERACALIBRATION1, writer); if (!singleIlluminant) { camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM2); - BAIL_IF_EMPTY(entry2, env, TAG_CAMERACALIBRATION2, writer); + BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_CAMERACALIBRATION2, writer); int32_t calibrationTransform2[entry2.count * 2]; ctr = 0; @@ -1369,7 +1503,7 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt calibrationTransform2[ctr++] = entry2.data.r[i].denominator; } - BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION2, entry2.count, + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CAMERACALIBRATION2, entry2.count, calibrationTransform2, TIFF_IFD_0), env, TAG_CAMERACALIBRATION2, writer); } } @@ -1378,7 +1512,7 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt // Set forward transforms camera_metadata_entry entry1 = characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1); - BAIL_IF_EMPTY(entry1, env, TAG_FORWARDMATRIX1, writer); + BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_FORWARDMATRIX1, writer); int32_t forwardTransform1[entry1.count * 2]; @@ -1388,13 +1522,13 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt forwardTransform1[ctr++] = entry1.data.r[i].denominator; } - BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX1, entry1.count, forwardTransform1, - TIFF_IFD_0), env, TAG_FORWARDMATRIX1, writer); + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FORWARDMATRIX1, entry1.count, + forwardTransform1, TIFF_IFD_0), env, TAG_FORWARDMATRIX1, writer); if (!singleIlluminant) { camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX2); - BAIL_IF_EMPTY(entry2, env, TAG_FORWARDMATRIX2, writer); + BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_FORWARDMATRIX2, writer); int32_t forwardTransform2[entry2.count * 2]; ctr = 0; @@ -1403,8 +1537,8 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt forwardTransform2[ctr++] = entry2.data.r[i].denominator; } - BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX2, entry2.count, forwardTransform2, - TIFF_IFD_0), env, TAG_FORWARDMATRIX2, writer); + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FORWARDMATRIX2, entry2.count, + forwardTransform2, TIFF_IFD_0), env, TAG_FORWARDMATRIX2, writer); } } @@ -1412,7 +1546,7 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt // Set camera neutral camera_metadata_entry entry = results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT); - BAIL_IF_EMPTY(entry, env, TAG_ASSHOTNEUTRAL, writer); + BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ASSHOTNEUTRAL, writer); uint32_t cameraNeutral[entry.count * 2]; size_t ctr = 0; @@ -1423,33 +1557,27 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt static_cast<uint32_t>(entry.data.r[i].denominator); } - BAIL_IF_INVALID(writer->addEntry(TAG_ASSHOTNEUTRAL, entry.count, cameraNeutral, + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ASSHOTNEUTRAL, entry.count, cameraNeutral, TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL, writer); } - { - // Setup data strips - // TODO: Switch to tiled implementation. - if (writer->addStrip(TIFF_IFD_0) != OK) { - ALOGE("%s: Could not setup strip tags.", __FUNCTION__); - jniThrowException(env, "java/lang/IllegalStateException", - "Failed to setup strip tags."); - return; - } - } { // Set dimensions + if (calculateAndSetCrop(env, characteristics, imageWidth, imageHeight, writer) != OK) { + return nullptr; + } camera_metadata_entry entry = characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE); - BAIL_IF_EMPTY(entry, env, TAG_DEFAULTCROPSIZE, writer); + BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_DEFAULTCROPSIZE, writer); uint32_t xmin = static_cast<uint32_t>(entry.data.i32[0]); uint32_t ymin = static_cast<uint32_t>(entry.data.i32[1]); uint32_t width = static_cast<uint32_t>(entry.data.i32[2]); uint32_t height = static_cast<uint32_t>(entry.data.i32[3]); - if (calculateAndSetCrop(env, characteristics, xmin, ymin, width, height, writer) != OK) { - return; - } + + uint32_t activeArea[] = {ymin, xmin, ymin + height, xmin + width}; + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ACTIVEAREA, 4, activeArea, TIFF_IFD_0), + env, TAG_ACTIVEAREA, writer); } { @@ -1469,7 +1597,7 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt cameraModel += "-"; cameraModel += brand; - BAIL_IF_INVALID(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1, + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1, reinterpret_cast<const uint8_t*>(cameraModel.string()), TIFF_IFD_0), env, TAG_UNIQUECAMERAMODEL, writer); } @@ -1486,7 +1614,7 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt if ((err = convertCFA(cfaEnum, /*out*/cfaOut)) != OK) { jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid CFA from camera characteristics"); - return; + return nullptr; } double noiseProfile[numPlaneColors * 2]; @@ -1500,8 +1628,9 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt if ((err = generateNoiseProfile(entry.data.d, cfaOut, numCfaChannels, cfaPlaneColor, numPlaneColors, /*out*/ noiseProfile)) == OK) { - BAIL_IF_INVALID(writer->addEntry(TAG_NOISEPROFILE, numPlaneColors * 2, - noiseProfile, TIFF_IFD_0), env, TAG_NOISEPROFILE, writer); + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NOISEPROFILE, + numPlaneColors * 2, noiseProfile, TIFF_IFD_0), env, TAG_NOISEPROFILE, + writer); } else { ALOGW("%s: Error converting coefficients for noise profile, no noise profile" " tag written...", __FUNCTION__); @@ -1533,19 +1662,26 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt camera_metadata_entry entry2 = results.find(ANDROID_STATISTICS_LENS_SHADING_MAP); + camera_metadata_entry entry = + characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE); + BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_IMAGEWIDTH, writer); + uint32_t xmin = static_cast<uint32_t>(entry.data.i32[0]); + uint32_t ymin = static_cast<uint32_t>(entry.data.i32[1]); + uint32_t width = static_cast<uint32_t>(entry.data.i32[2]); + uint32_t height = static_cast<uint32_t>(entry.data.i32[3]); if (entry2.count > 0 && entry2.count == lsmWidth * lsmHeight * 4) { err = builder.addGainMapsForMetadata(lsmWidth, lsmHeight, - 0, - 0, - imageHeight, - imageWidth, + ymin, + xmin, + height, + width, opcodeCfaLayout, entry2.data.f); if (err != OK) { ALOGE("%s: Could not add Lens shading map.", __FUNCTION__); jniThrowRuntimeException(env, "failed to add lens shading map."); - return; + return nullptr; } } @@ -1553,14 +1689,14 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt uint8_t opcodeListBuf[listSize]; err = builder.buildOpList(opcodeListBuf); if (err == OK) { - BAIL_IF_INVALID(writer->addEntry(TAG_OPCODELIST2, listSize, opcodeListBuf, + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_OPCODELIST2, listSize, opcodeListBuf, TIFF_IFD_0), env, TAG_OPCODELIST2, writer); } else { ALOGE("%s: Could not build list of opcodes for distortion correction and lens shading" "map.", __FUNCTION__); jniThrowRuntimeException(env, "failed to construct opcode list for distortion" " correction and lens shading map"); - return; + return nullptr; } } @@ -1578,12 +1714,12 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt if (entry3.count == 6 && entry4.count == 5) { float cx = entry4.data.f[/*c_x*/2]; float cy = entry4.data.f[/*c_y*/3]; - err = builder.addWarpRectilinearForMetadata(entry3.data.f, imageWidth, imageHeight, cx, + err = builder.addWarpRectilinearForMetadata(entry3.data.f, preWidth, preHeight, cx, cy); if (err != OK) { ALOGE("%s: Could not add distortion correction.", __FUNCTION__); jniThrowRuntimeException(env, "failed to add distortion correction."); - return; + return nullptr; } } @@ -1591,30 +1727,236 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt uint8_t opcodeListBuf[listSize]; err = builder.buildOpList(opcodeListBuf); if (err == OK) { - BAIL_IF_INVALID(writer->addEntry(TAG_OPCODELIST3, listSize, opcodeListBuf, + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_OPCODELIST3, listSize, opcodeListBuf, TIFF_IFD_0), env, TAG_OPCODELIST3, writer); } else { ALOGE("%s: Could not build list of opcodes for distortion correction and lens shading" "map.", __FUNCTION__); jniThrowRuntimeException(env, "failed to construct opcode list for distortion" " correction and lens shading map"); - return; + return nullptr; } } - DngCreator_setNativeContext(env, thiz, nativeContext); + { + // Set up orientation tags. + uint16_t orientation = nativeContext->getOrientation(); + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), + env, TAG_ORIENTATION, writer); + + } + + if (nativeContext->hasDescription()){ + // Set Description + String8 description = nativeContext->getDescription(); + size_t len = description.bytes() + 1; + if (writer->addEntry(TAG_IMAGEDESCRIPTION, len, + reinterpret_cast<const uint8_t*>(description.string()), TIFF_IFD_0) != OK) { + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", + "Invalid metadata for tag %x", TAG_IMAGEDESCRIPTION); + } + } + + if (nativeContext->hasGpsData()) { + // Set GPS tags + GpsData gpsData = nativeContext->getGpsData(); + if (!writer->hasIfd(TIFF_IFD_GPSINFO)) { + if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_GPSINFO, TiffWriter::GPSINFO) != OK) { + ALOGE("%s: Failed to add GpsInfo IFD %u to IFD %u", __FUNCTION__, TIFF_IFD_GPSINFO, + TIFF_IFD_0); + jniThrowException(env, "java/lang/IllegalStateException", "Failed to add GPSINFO"); + return nullptr; + } + } + + { + uint8_t version[] = {2, 3, 0, 0}; + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSVERSIONID, 4, version, + TIFF_IFD_GPSINFO), env, TAG_GPSVERSIONID, writer); + } + + { + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLATITUDEREF, + GpsData::GPS_REF_LENGTH, gpsData.mLatitudeRef, TIFF_IFD_GPSINFO), env, + TAG_GPSLATITUDEREF, writer); + } + + { + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLONGITUDEREF, + GpsData::GPS_REF_LENGTH, gpsData.mLongitudeRef, TIFF_IFD_GPSINFO), env, + TAG_GPSLONGITUDEREF, writer); + } + + { + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLATITUDE, 3, gpsData.mLatitude, + TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDE, writer); + } + + { + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLONGITUDE, 3, gpsData.mLongitude, + TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDE, writer); + } + + { + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSTIMESTAMP, 3, gpsData.mTimestamp, + TIFF_IFD_GPSINFO), env, TAG_GPSTIMESTAMP, writer); + } + + { + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSDATESTAMP, + GpsData::GPS_DATE_LENGTH, gpsData.mDate, TIFF_IFD_GPSINFO), env, + TAG_GPSDATESTAMP, writer); + } + } + + + if (nativeContext->hasThumbnail()) { + if (!writer->hasIfd(TIFF_IFD_SUB1)) { + if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_SUB1) != OK) { + ALOGE("%s: Failed to add SubIFD %u to IFD %u", __FUNCTION__, TIFF_IFD_SUB1, + TIFF_IFD_0); + jniThrowException(env, "java/lang/IllegalStateException", "Failed to add SubIFD"); + return nullptr; + } + } + + Vector<uint16_t> tagsToMove; + tagsToMove.add(TAG_ORIENTATION); + tagsToMove.add(TAG_NEWSUBFILETYPE); + tagsToMove.add(TAG_ACTIVEAREA); + tagsToMove.add(TAG_BITSPERSAMPLE); + tagsToMove.add(TAG_COMPRESSION); + tagsToMove.add(TAG_IMAGEWIDTH); + tagsToMove.add(TAG_IMAGELENGTH); + tagsToMove.add(TAG_PHOTOMETRICINTERPRETATION); + tagsToMove.add(TAG_BLACKLEVEL); + tagsToMove.add(TAG_BLACKLEVELREPEATDIM); + tagsToMove.add(TAG_SAMPLESPERPIXEL); + tagsToMove.add(TAG_PLANARCONFIGURATION); + tagsToMove.add(TAG_CFAREPEATPATTERNDIM); + tagsToMove.add(TAG_CFAPATTERN); + tagsToMove.add(TAG_CFAPLANECOLOR); + tagsToMove.add(TAG_CFALAYOUT); + tagsToMove.add(TAG_XRESOLUTION); + tagsToMove.add(TAG_YRESOLUTION); + tagsToMove.add(TAG_RESOLUTIONUNIT); + tagsToMove.add(TAG_WHITELEVEL); + tagsToMove.add(TAG_DEFAULTSCALE); + tagsToMove.add(TAG_DEFAULTCROPORIGIN); + tagsToMove.add(TAG_DEFAULTCROPSIZE); + tagsToMove.add(TAG_OPCODELIST2); + tagsToMove.add(TAG_OPCODELIST3); + + if (moveEntries(writer, TIFF_IFD_0, TIFF_IFD_SUB1, tagsToMove) != OK) { + jniThrowException(env, "java/lang/IllegalStateException", "Failed to move entries"); + return nullptr; + } + + // Make sure both IFDs get the same orientation tag + sp<TiffEntry> orientEntry = writer->getEntry(TAG_ORIENTATION, TIFF_IFD_SUB1); + if (orientEntry.get() != nullptr) { + writer->addEntry(orientEntry, TIFF_IFD_0); + } + + // Setup thumbnail tags + + { + // Set photometric interpretation + uint16_t interpretation = 2; // RGB + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, + &interpretation, TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer); + } + + { + // Set planar configuration + uint16_t config = 1; // Chunky + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, + TIFF_IFD_0), env, TAG_PLANARCONFIGURATION, writer); + } + + { + // Set samples per pixel + uint16_t samples = SAMPLES_PER_RGB_PIXEL; + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, + TIFF_IFD_0), env, TAG_SAMPLESPERPIXEL, writer); + } + + { + // Set bits per sample + uint16_t bits = BITS_PER_RGB_SAMPLE; + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), + env, TAG_BITSPERSAMPLE, writer); + } + + { + // Set subfiletype + uint32_t subfileType = 1; // Thumbnail image + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, + TIFF_IFD_0), env, TAG_NEWSUBFILETYPE, writer); + } + + { + // Set compression + uint16_t compression = 1; // None + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COMPRESSION, 1, &compression, + TIFF_IFD_0), env, TAG_COMPRESSION, writer); + } + + { + // Set dimensions + uint32_t uWidth = nativeContext->getThumbnailWidth(); + uint32_t uHeight = nativeContext->getThumbnailHeight(); + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEWIDTH, 1, &uWidth, TIFF_IFD_0), + env, TAG_IMAGEWIDTH, writer); + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGELENGTH, 1, &uHeight, TIFF_IFD_0), + env, TAG_IMAGELENGTH, writer); + } + + { + // x resolution + uint32_t xres[] = { 72, 1 }; // default 72 ppi + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0), + env, TAG_XRESOLUTION, writer); + + // y resolution + uint32_t yres[] = { 72, 1 }; // default 72 ppi + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0), + env, TAG_YRESOLUTION, writer); + + uint16_t unit = 2; // inches + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0), + env, TAG_RESOLUTIONUNIT, writer); + } + } + + if (writer->addStrip(TIFF_IFD_0) != OK) { + ALOGE("%s: Could not setup thumbnail strip tags.", __FUNCTION__); + jniThrowException(env, "java/lang/IllegalStateException", + "Failed to setup thumbnail strip tags."); + return nullptr; + } + + if (writer->hasIfd(TIFF_IFD_SUB1)) { + if (writer->addStrip(TIFF_IFD_SUB1) != OK) { + ALOGE("%s: Could not main image strip tags.", __FUNCTION__); + jniThrowException(env, "java/lang/IllegalStateException", + "Failed to setup main image strip tags."); + return nullptr; + } + } + return writer; } static void DngCreator_destroy(JNIEnv* env, jobject thiz) { ALOGV("%s:", __FUNCTION__); - DngCreator_setNativeContext(env, thiz, NULL); + DngCreator_setNativeContext(env, thiz, nullptr); } static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz, jint orient) { ALOGV("%s:", __FUNCTION__); - TiffWriter* writer = DngCreator_getCreator(env, thiz); - if (writer == NULL) { + NativeContext* context = DngCreator_getNativeContext(env, thiz); + if (context == nullptr) { ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__); jniThrowException(env, "java/lang/AssertionError", "setOrientation called with uninitialized DngCreator"); @@ -1622,138 +1964,73 @@ static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz, jint orie } uint16_t orientation = static_cast<uint16_t>(orient); - BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), env, - TAG_ORIENTATION, writer); - - // Set main image orientation also if in a separate IFD - if (writer->hasIfd(TIFF_IFD_SUB1)) { - BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_SUB1), env, - TAG_ORIENTATION, writer); - } + context->setOrientation(orientation); } static void DngCreator_nativeSetDescription(JNIEnv* env, jobject thiz, jstring description) { ALOGV("%s:", __FUNCTION__); - TiffWriter* writer = DngCreator_getCreator(env, thiz); - if (writer == NULL) { + NativeContext* context = DngCreator_getNativeContext(env, thiz); + if (context == nullptr) { ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__); jniThrowException(env, "java/lang/AssertionError", "setDescription called with uninitialized DngCreator"); return; } - const char* desc = env->GetStringUTFChars(description, NULL); - size_t len = strlen(desc) + 1; - - if (writer->addEntry(TAG_IMAGEDESCRIPTION, len, - reinterpret_cast<const uint8_t*>(desc), TIFF_IFD_0) != OK) { - jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", - "Invalid metadata for tag %x", TAG_IMAGEDESCRIPTION); - } - + const char* desc = env->GetStringUTFChars(description, nullptr); + context->setDescription(String8(desc)); env->ReleaseStringUTFChars(description, desc); } -static void DngCreator_nativeSetGpsTags(JNIEnv* env, jobject thiz, jintArray latTag, jstring latRef, - jintArray longTag, jstring longRef, jstring dateTag, jintArray timeTag) { +static void DngCreator_nativeSetGpsTags(JNIEnv* env, jobject thiz, jintArray latTag, + jstring latRef, jintArray longTag, jstring longRef, jstring dateTag, jintArray timeTag) { ALOGV("%s:", __FUNCTION__); - TiffWriter* writer = DngCreator_getCreator(env, thiz); - if (writer == NULL) { + NativeContext* context = DngCreator_getNativeContext(env, thiz); + if (context == nullptr) { ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__); jniThrowException(env, "java/lang/AssertionError", "setGpsTags called with uninitialized DngCreator"); return; } - if (!writer->hasIfd(TIFF_IFD_GPSINFO)) { - if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_GPSINFO, TiffWriter::GPSINFO) != OK) { - ALOGE("%s: Failed to add GpsInfo IFD %u to IFD %u", __FUNCTION__, TIFF_IFD_GPSINFO, - TIFF_IFD_0); - jniThrowException(env, "java/lang/IllegalStateException", "Failed to add GPSINFO"); - return; - } - } + GpsData data; - const jsize GPS_VALUE_LENGTH = 6; jsize latLen = env->GetArrayLength(latTag); jsize longLen = env->GetArrayLength(longTag); jsize timeLen = env->GetArrayLength(timeTag); - if (latLen != GPS_VALUE_LENGTH) { + if (latLen != GpsData::GPS_VALUE_LENGTH) { jniThrowException(env, "java/lang/IllegalArgumentException", "invalid latitude tag length"); return; - } else if (longLen != GPS_VALUE_LENGTH) { + } else if (longLen != GpsData::GPS_VALUE_LENGTH) { jniThrowException(env, "java/lang/IllegalArgumentException", "invalid longitude tag length"); return; - } else if (timeLen != GPS_VALUE_LENGTH) { + } else if (timeLen != GpsData::GPS_VALUE_LENGTH) { jniThrowException(env, "java/lang/IllegalArgumentException", "invalid time tag length"); return; } - uint32_t latitude[GPS_VALUE_LENGTH]; - uint32_t longitude[GPS_VALUE_LENGTH]; - uint32_t timestamp[GPS_VALUE_LENGTH]; - - env->GetIntArrayRegion(latTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH), - reinterpret_cast<jint*>(&latitude)); - env->GetIntArrayRegion(longTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH), - reinterpret_cast<jint*>(&longitude)); - env->GetIntArrayRegion(timeTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH), - reinterpret_cast<jint*>(×tamp)); - - const jsize GPS_REF_LENGTH = 2; - const jsize GPS_DATE_LENGTH = 11; - uint8_t latitudeRef[GPS_REF_LENGTH]; - uint8_t longitudeRef[GPS_REF_LENGTH]; - uint8_t date[GPS_DATE_LENGTH]; - - env->GetStringUTFRegion(latRef, 0, 1, reinterpret_cast<char*>(&latitudeRef)); - latitudeRef[GPS_REF_LENGTH - 1] = '\0'; - env->GetStringUTFRegion(longRef, 0, 1, reinterpret_cast<char*>(&longitudeRef)); - longitudeRef[GPS_REF_LENGTH - 1] = '\0'; - - env->GetStringUTFRegion(dateTag, 0, GPS_DATE_LENGTH - 1, reinterpret_cast<char*>(&date)); - date[GPS_DATE_LENGTH - 1] = '\0'; - - { - uint8_t version[] = {2, 3, 0, 0}; - BAIL_IF_INVALID(writer->addEntry(TAG_GPSVERSIONID, 4, version, - TIFF_IFD_GPSINFO), env, TAG_GPSVERSIONID, writer); - } - - { - BAIL_IF_INVALID(writer->addEntry(TAG_GPSLATITUDEREF, GPS_REF_LENGTH, latitudeRef, - TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDEREF, writer); - } - - { - BAIL_IF_INVALID(writer->addEntry(TAG_GPSLONGITUDEREF, GPS_REF_LENGTH, longitudeRef, - TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDEREF, writer); - } - - { - BAIL_IF_INVALID(writer->addEntry(TAG_GPSLATITUDE, 3, latitude, - TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDE, writer); - } + env->GetIntArrayRegion(latTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH), + reinterpret_cast<jint*>(&data.mLatitude)); + env->GetIntArrayRegion(longTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH), + reinterpret_cast<jint*>(&data.mLongitude)); + env->GetIntArrayRegion(timeTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH), + reinterpret_cast<jint*>(&data.mTimestamp)); - { - BAIL_IF_INVALID(writer->addEntry(TAG_GPSLONGITUDE, 3, longitude, - TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDE, writer); - } - { - BAIL_IF_INVALID(writer->addEntry(TAG_GPSTIMESTAMP, 3, timestamp, - TIFF_IFD_GPSINFO), env, TAG_GPSTIMESTAMP, writer); - } + env->GetStringUTFRegion(latRef, 0, 1, reinterpret_cast<char*>(&data.mLatitudeRef)); + data.mLatitudeRef[GpsData::GPS_REF_LENGTH - 1] = '\0'; + env->GetStringUTFRegion(longRef, 0, 1, reinterpret_cast<char*>(&data.mLongitudeRef)); + data.mLongitudeRef[GpsData::GPS_REF_LENGTH - 1] = '\0'; + env->GetStringUTFRegion(dateTag, 0, GpsData::GPS_DATE_LENGTH - 1, + reinterpret_cast<char*>(&data.mDate)); + data.mDate[GpsData::GPS_DATE_LENGTH - 1] = '\0'; - { - BAIL_IF_INVALID(writer->addEntry(TAG_GPSDATESTAMP, GPS_DATE_LENGTH, date, - TIFF_IFD_GPSINFO), env, TAG_GPSDATESTAMP, writer); - } + context->setGpsData(data); } static void DngCreator_nativeSetThumbnail(JNIEnv* env, jobject thiz, jobject buffer, jint width, @@ -1761,8 +2038,7 @@ static void DngCreator_nativeSetThumbnail(JNIEnv* env, jobject thiz, jobject buf ALOGV("%s:", __FUNCTION__); NativeContext* context = DngCreator_getNativeContext(env, thiz); - TiffWriter* writer = DngCreator_getCreator(env, thiz); - if (writer == NULL || context == NULL) { + if (context == nullptr) { ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__); jniThrowException(env, "java/lang/AssertionError", "setThumbnail called with uninitialized DngCreator"); @@ -1779,147 +2055,12 @@ static void DngCreator_nativeSetThumbnail(JNIEnv* env, jobject thiz, jobject buf } uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer)); - if (pixelBytes == NULL) { + if (pixelBytes == nullptr) { ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__); jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer"); return; } - if (!writer->hasIfd(TIFF_IFD_SUB1)) { - if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_SUB1) != OK) { - ALOGE("%s: Failed to add SubIFD %u to IFD %u", __FUNCTION__, TIFF_IFD_SUB1, - TIFF_IFD_0); - jniThrowException(env, "java/lang/IllegalStateException", "Failed to add SubIFD"); - return; - } - - Vector<uint16_t> tagsToMove; - tagsToMove.add(TAG_ORIENTATION); - tagsToMove.add(TAG_NEWSUBFILETYPE); - tagsToMove.add(TAG_BITSPERSAMPLE); - tagsToMove.add(TAG_COMPRESSION); - tagsToMove.add(TAG_IMAGEWIDTH); - tagsToMove.add(TAG_IMAGELENGTH); - tagsToMove.add(TAG_PHOTOMETRICINTERPRETATION); - tagsToMove.add(TAG_BLACKLEVEL); - tagsToMove.add(TAG_BLACKLEVELREPEATDIM); - tagsToMove.add(TAG_SAMPLESPERPIXEL); - tagsToMove.add(TAG_PLANARCONFIGURATION); - tagsToMove.add(TAG_CFAREPEATPATTERNDIM); - tagsToMove.add(TAG_CFAPATTERN); - tagsToMove.add(TAG_CFAPLANECOLOR); - tagsToMove.add(TAG_CFALAYOUT); - tagsToMove.add(TAG_XRESOLUTION); - tagsToMove.add(TAG_YRESOLUTION); - tagsToMove.add(TAG_RESOLUTIONUNIT); - tagsToMove.add(TAG_WHITELEVEL); - tagsToMove.add(TAG_DEFAULTSCALE); - tagsToMove.add(TAG_ROWSPERSTRIP); - tagsToMove.add(TAG_STRIPBYTECOUNTS); - tagsToMove.add(TAG_STRIPOFFSETS); - tagsToMove.add(TAG_DEFAULTCROPORIGIN); - tagsToMove.add(TAG_DEFAULTCROPSIZE); - tagsToMove.add(TAG_OPCODELIST2); - tagsToMove.add(TAG_OPCODELIST3); - - if (moveEntries(writer, TIFF_IFD_0, TIFF_IFD_SUB1, tagsToMove) != OK) { - jniThrowException(env, "java/lang/IllegalStateException", "Failed to move entries"); - return; - } - - // Make sure both IFDs get the same orientation tag - sp<TiffEntry> orientEntry = writer->getEntry(TAG_ORIENTATION, TIFF_IFD_SUB1); - if (orientEntry != NULL) { - writer->addEntry(orientEntry, TIFF_IFD_0); - } - } - - // Setup thumbnail tags - - { - // Set photometric interpretation - uint16_t interpretation = 2; // RGB - BAIL_IF_INVALID(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, &interpretation, - TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer); - } - - { - // Set planar configuration - uint16_t config = 1; // Chunky - BAIL_IF_INVALID(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, TIFF_IFD_0), - env, TAG_PLANARCONFIGURATION, writer); - } - - { - // Set samples per pixel - uint16_t samples = SAMPLES_PER_RGB_PIXEL; - BAIL_IF_INVALID(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0), - env, TAG_SAMPLESPERPIXEL, writer); - } - - { - // Set bits per sample - uint16_t bits = BITS_PER_RGB_SAMPLE; - BAIL_IF_INVALID(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env, - TAG_BITSPERSAMPLE, writer); - } - - { - // Set subfiletype - uint32_t subfileType = 1; // Thumbnail image - BAIL_IF_INVALID(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, TIFF_IFD_0), env, - TAG_NEWSUBFILETYPE, writer); - } - - { - // Set compression - uint16_t compression = 1; // None - BAIL_IF_INVALID(writer->addEntry(TAG_COMPRESSION, 1, &compression, TIFF_IFD_0), env, - TAG_COMPRESSION, writer); - } - - { - // Set dimensions - uint32_t uWidth = static_cast<uint32_t>(width); - uint32_t uHeight = static_cast<uint32_t>(height); - BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEWIDTH, 1, &uWidth, TIFF_IFD_0), env, - TAG_IMAGEWIDTH, writer); - BAIL_IF_INVALID(writer->addEntry(TAG_IMAGELENGTH, 1, &uHeight, TIFF_IFD_0), env, - TAG_IMAGELENGTH, writer); - } - - { - // x resolution - uint32_t xres[] = { 72, 1 }; // default 72 ppi - BAIL_IF_INVALID(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0), - env, TAG_XRESOLUTION, writer); - - // y resolution - uint32_t yres[] = { 72, 1 }; // default 72 ppi - BAIL_IF_INVALID(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0), - env, TAG_YRESOLUTION, writer); - - uint16_t unit = 2; // inches - BAIL_IF_INVALID(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0), - env, TAG_RESOLUTIONUNIT, writer); - } - - { - // Setup data strips - if (writer->addStrip(TIFF_IFD_0) != OK) { - ALOGE("%s: Could not setup thumbnail strip tags.", __FUNCTION__); - jniThrowException(env, "java/lang/IllegalStateException", - "Failed to setup thumbnail strip tags."); - return; - } - if (writer->addStrip(TIFF_IFD_SUB1) != OK) { - ALOGE("%s: Could not main image strip tags.", __FUNCTION__); - jniThrowException(env, "java/lang/IllegalStateException", - "Failed to setup main image strip tags."); - return; - } - } - if (!context->setThumbnail(pixelBytes, width, height)) { jniThrowException(env, "java/lang/IllegalStateException", "Failed to set thumbnail."); @@ -1947,16 +2088,20 @@ static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outSt return; } - TiffWriter* writer = DngCreator_getCreator(env, thiz); NativeContext* context = DngCreator_getNativeContext(env, thiz); - if (writer == NULL || context == NULL) { + if (context == nullptr) { ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__); jniThrowException(env, "java/lang/AssertionError", "Write called with uninitialized DngCreator"); return; } + sp<TiffWriter> writer = DngCreator_setup(env, thiz, uWidth, uHeight); - // Validate DNG header + if (writer.get() == nullptr) { + return; + } + + // Validate DNG size if (!validateDngHeader(env, writer, *(context->getCharacteristics()), width, height)) { return; } @@ -1991,7 +2136,7 @@ static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outSt } uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer)); - if (pixelBytes == NULL) { + if (pixelBytes == nullptr) { ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__); jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer"); return; @@ -2051,16 +2196,20 @@ static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject return; } - TiffWriter* writer = DngCreator_getCreator(env, thiz); NativeContext* context = DngCreator_getNativeContext(env, thiz); - if (writer == NULL || context == NULL) { + if (context == nullptr) { ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__); jniThrowException(env, "java/lang/AssertionError", "Write called with uninitialized DngCreator"); return; } + sp<TiffWriter> writer = DngCreator_setup(env, thiz, uWidth, uHeight); + + if (writer.get() == nullptr) { + return; + } - // Validate DNG header + // Validate DNG size if (!validateDngHeader(env, writer, *(context->getCharacteristics()), width, height)) { return; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 92862f5..3e22e09 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1749,6 +1749,10 @@ <!-- ==================================== --> <eat-comment /> + <!-- @SystemApi Allows access to the list of accounts in the Accounts Service. --> + <permission android:name="android.permission.GET_ACCOUNTS_PRIVILEGED" + android:protectionLevel="signature|privileged" /> + <!-- @SystemApi Allows applications to RW to diagnostic resources. <p>Not for use by third-party applications. --> <permission android:name="android.permission.DIAGNOSTIC" diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index be2b962..0533317 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -1001,7 +1001,7 @@ <string name="sms_short_code_remember_undo_instruction" msgid="4960944133052287484">"Puedes cambiar esta opción más tarde en Ajustes > Aplicaciones."</string> <string name="sms_short_code_confirm_always_allow" msgid="3241181154869493368">"Permitir siempre"</string> <string name="sms_short_code_confirm_never_allow" msgid="446992765774269673">"No permitir nunca"</string> - <string name="sim_removed_title" msgid="6227712319223226185">"Tarjeta SIM eliminada"</string> + <string name="sim_removed_title" msgid="6227712319223226185">"Tarjeta SIM retirada"</string> <string name="sim_removed_message" msgid="5450336489923274918">"La red móvil no estará disponible hasta que reinicies el dispositivo con una tarjeta SIM válida insertada."</string> <string name="sim_done_button" msgid="827949989369963775">"Listo"</string> <string name="sim_added_title" msgid="3719670512889674693">"Tarjeta SIM añadida"</string> diff --git a/core/res/res/values-mcc310-mnc260-pt-rBR/strings.xml b/core/res/res/values-mcc310-mnc260-pt-rBR/strings.xml new file mode 100644 index 0000000..bad49c3 --- /dev/null +++ b/core/res/res/values-mcc310-mnc260-pt-rBR/strings.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** Copyright 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 my 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. +*/ + --> + +<!-- These resources are around just to allow their values to be customized + for different hardware and product builds. --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string-array name="wfcOperatorErrorAlertMessages"> + <item msgid="7239039348648848288">"Para fazer chamadas e enviar mensagens por Wi-Fi, primeiro peça à sua operadora para configurar esse serviço. Depois ative novamente as chamadas por Wi-Fi nas configurações."</item> + </string-array> + <string-array name="wfcOperatorErrorNotificationMessages"> + <item msgid="483847327467331298">"Faça registro na sua operadora"</item> + </string-array> + <string name="wfcSpnFormat" msgid="4982938551498609442">"%s chamada Wi-Fi"</string> +</resources> diff --git a/core/res/res/values-mcc425/config.xml b/core/res/res/values-mcc425/config.xml new file mode 100644 index 0000000..95d30a4 --- /dev/null +++ b/core/res/res/values-mcc425/config.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2014, 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. +*/ +--> +<resources> + <bool name="config_use_sim_language_file">false</bool> +</resources> diff --git a/core/res/res/values-mcc432/config.xml b/core/res/res/values-mcc432/config.xml new file mode 100644 index 0000000..95d30a4 --- /dev/null +++ b/core/res/res/values-mcc432/config.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2014, 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. +*/ +--> +<resources> + <bool name="config_use_sim_language_file">false</bool> +</resources> diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml index be72354..df9f2805 100644 --- a/core/res/res/values-mn-rMN/strings.xml +++ b/core/res/res/values-mn-rMN/strings.xml @@ -1042,7 +1042,7 @@ <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB төхөөрөмжид холбогдов"</string> <string name="usb_notification_message" msgid="7347368030849048437">"Нэмэлт сонголтыг харахын тулд дарна."</string> <string name="adb_active_notification_title" msgid="6729044778949189918">"USB дебаг холбогдсон"</string> - <string name="adb_active_notification_message" msgid="1016654627626476142">"USB дебаг хийхийг идэвхгүй болгох бол хүрнэ үү."</string> + <string name="adb_active_notification_message" msgid="1016654627626476142">"USB дебагийг идэвхгүй болгох бол хүрнэ үү."</string> <string name="select_input_method" msgid="8547250819326693584">"Гарыг өөрчлөх"</string> <string name="configure_input_methods" msgid="4769971288371946846">"Гар сонгох"</string> <string name="show_ime" msgid="9157568568695230830">"Оруулах аргыг харуулах"</string> diff --git a/core/res/res/values-pt-rBR-watch/strings.xml b/core/res/res/values-pt-rBR-watch/strings.xml new file mode 100644 index 0000000..120e4a5 --- /dev/null +++ b/core/res/res/values-pt-rBR-watch/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* //device/apps/common/assets/res/any/strings.xml +** +** Copyright 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. +*/ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="android_upgrading_apk" msgid="1090732262010398759">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string> +</resources> diff --git a/core/res/res/values-sq-rAL/strings.xml b/core/res/res/values-sq-rAL/strings.xml index 549c5ef7..2bde626 100644 --- a/core/res/res/values-sq-rAL/strings.xml +++ b/core/res/res/values-sq-rAL/strings.xml @@ -21,7 +21,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="byteShort" msgid="8340973892742019101">"B"</string> - <string name="kilobyteShort" msgid="5973789783504771878">"kilobajt"</string> + <string name="kilobyteShort" msgid="5973789783504771878">"KB"</string> <string name="megabyteShort" msgid="6355851576770428922">"MB"</string> <string name="gigabyteShort" msgid="3259882455212193214">"GB"</string> <string name="terabyteShort" msgid="231613018159186962">"terabajt"</string> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 06b6389..54848e9 100755 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -408,6 +408,7 @@ <integer translatable="false" name="config_wifi_framework_wifi_score_good_link_speed_24">24</integer> <integer translatable="false" name="config_wifi_framework_wifi_score_good_link_speed_5">36</integer> <string translatable="false" name="config_wifi_random_mac_oui">DA-A1-19</string> + <string translatable="false" name="config_wifi_framework_sap_2G_channel_list">1,6,11</string> <bool translatable="false" name="config_wifi_framework_cellular_handover_enable_user_triggered_adjustment">true</bool> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 568e61e..85b6c12 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2652,4 +2652,6 @@ <public type="id" name="accessibilityActionScrollDown" id="0x0102003a" /> <public type="id" name="accessibilityActionScrollRight" id="0x0102003b" /> <public type="id" name="accessibilityActionContextClick" id="0x0102003c" /> + + <public type="string" name="fingerprint_icon_content_description" /> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 35c1f0e..a45a0fa 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1221,6 +1221,9 @@ <string-array name="fingerprint_error_vendor"> </string-array> + <!-- Content description which should be used for the fingerprint icon. --> + <string name="fingerprint_icon_content_description">Fingerprint icon</string> + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_readSyncSettings">read sync settings</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index fe82b8c..8070986 100755 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -337,6 +337,7 @@ <java-symbol type="integer" name="config_wifi_active_rx_cur_ma" /> <java-symbol type="integer" name="config_wifi_tx_cur_ma" /> <java-symbol type="integer" name="config_wifi_operating_voltage_mv" /> + <java-symbol type="string" name="config_wifi_framework_sap_2G_channel_list" /> <java-symbol type="bool" name="editable_voicemailnumber" /> |
