From: Chih-Wei Huang Date: Thu, 14 Jun 2018 09:30:24 +0000 (+0800) Subject: Merge tag 'android-8.1.0_r33' into oreo-x86 X-Git-Tag: android-x86-8.1-r1~6 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=d8bd4bccb1d15fddc822d416d45f5cf089190fac;hp=62b35aba4cd284f90ed14d54f7095cb553eba32e;p=android-x86%2Fframeworks-base.git Merge tag 'android-8.1.0_r33' into oreo-x86 Android 8.1.0 release 33 --- diff --git a/Android.mk b/Android.mk index dfcfa2b688e8..e3cc2b53be1c 100644 --- a/Android.mk +++ b/Android.mk @@ -623,6 +623,8 @@ LOCAL_PROTOC_OPTIMIZE_TYPE := stream LOCAL_PROTOC_FLAGS := \ -Iexternal/protobuf/src +LOCAL_STATIC_JAVA_LIBRARIES += analytics-utils + LOCAL_MODULE := framework LOCAL_DX_FLAGS := --core-library --multi-dex diff --git a/cmds/input/input b/cmds/input/input index 7f1a18e369c6..54ab94762e5c 100755 --- a/cmds/input/input +++ b/cmds/input/input @@ -1,3 +1,4 @@ +#!/system/bin/sh # Script to start "input" on the device, which has a very rudimentary # shell. # diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java index 9ee11f8571e2..a5899036f4bf 100644 --- a/cmds/input/src/com/android/commands/input/Input.java +++ b/cmds/input/src/com/android/commands/input/Input.java @@ -198,9 +198,12 @@ public class Input { injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, inputSource)); if (longpress) { - injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 1, 0, - KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_LONG_PRESS, - inputSource)); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + now = SystemClock.uptimeMillis(); } injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_UP, keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, inputSource)); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 8dc558ccf755..b3969cb78511 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -126,6 +126,8 @@ import com.android.internal.app.WindowDecorActionBar; import com.android.internal.policy.DecorView; import com.android.internal.policy.PhoneWindow; +import org.android_x86.analytics.AnalyticsHelper; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -759,6 +761,7 @@ public class Activity extends ContextThemeWrapper boolean mFinished; boolean mStartedActivity; private boolean mDestroyed; + private boolean mAppsStatistics; private boolean mDoReportFullyDrawn = true; private boolean mRestoredFromBundle; @@ -1018,6 +1021,7 @@ public class Activity extends ContextThemeWrapper } mRestoredFromBundle = savedInstanceState != null; mCalled = true; + mAppsStatistics = SystemProperties.getBoolean("persist.sys.apps_statistics", false); } /** @@ -1242,6 +1246,12 @@ public class Activity extends ContextThemeWrapper mFragments.doLoaderStart(); + // region @android-x86-analytics + // screen view + if (mAppsStatistics) { + AnalyticsHelper.hitScreen(this); + } + // endregion getApplication().dispatchActivityStarted(this); if (mAutoFillResetNeeded) { diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 931b5c913851..26b7d7f3478d 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -4180,6 +4180,8 @@ public class Camera { return Float.parseFloat(mMap.get(key)); } catch (NumberFormatException ex) { return defaultValue; + } catch (NullPointerException ex) { + return defaultValue; } } @@ -4189,6 +4191,8 @@ public class Camera { return Integer.parseInt(mMap.get(key)); } catch (NumberFormatException ex) { return defaultValue; + } catch (NullPointerException ex) { + return defaultValue; } } diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java index 5423ca97eb28..0063360ccd83 100644 --- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java +++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java @@ -405,7 +405,7 @@ public class LegacyMetadataMapper { /* * control.aeAvailableTargetFpsRanges */ - { + try { List fpsRanges = p.getSupportedPreviewFpsRange(); if (fpsRanges == null) { throw new AssertionError("Supported FPS ranges cannot be null."); @@ -422,6 +422,8 @@ public class LegacyMetadataMapper { (int) Math.ceil(r[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] / 1000.0)); } m.set(CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, ranges); + } catch (Exception e) { + Log.d(TAG, "ignore any error from getSupportedPreviewFpsRange: ", e); } /* diff --git a/core/java/android/os/storage/DiskInfo.java b/core/java/android/os/storage/DiskInfo.java index 911410744bd6..449739755a47 100644 --- a/core/java/android/os/storage/DiskInfo.java +++ b/core/java/android/os/storage/DiskInfo.java @@ -47,6 +47,7 @@ public class DiskInfo implements Parcelable { public static final int FLAG_DEFAULT_PRIMARY = 1 << 1; public static final int FLAG_SD = 1 << 2; public static final int FLAG_USB = 1 << 3; + public static final int FLAG_CDROM = 1 << 6; public final String id; public final int flags; @@ -107,6 +108,12 @@ public class DiskInfo implements Parcelable { } else { return res.getString(com.android.internal.R.string.storage_usb_drive); } + } else if ((flags & FLAG_CDROM) != 0) { + if (isInteresting(label)) { + return res.getString(com.android.internal.R.string.storage_cdrom_label, label); + } else { + return res.getString(com.android.internal.R.string.storage_cdrom); + } } else { return null; } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 91e2f7d4ddd0..6c7455d35397 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -3866,6 +3866,7 @@ public abstract class AbsListView extends AdapterView implements Te private void onTouchDown(MotionEvent ev) { mHasPerformedLongPress = false; mActivePointerId = ev.getPointerId(0); + hideSelector(); if (mTouchMode == TOUCH_MODE_OVERFLING) { // Stopped the fling. It is a scroll. @@ -5226,17 +5227,21 @@ public abstract class AbsListView extends AdapterView implements Te } mRecycler.fullyDetachScrapViews(); + boolean selectorOnScreen = false; if (!inTouchMode && mSelectedPosition != INVALID_POSITION) { final int childIndex = mSelectedPosition - mFirstPosition; if (childIndex >= 0 && childIndex < getChildCount()) { positionSelector(mSelectedPosition, getChildAt(childIndex)); + selectorOnScreen = true; } } else if (mSelectorPosition != INVALID_POSITION) { final int childIndex = mSelectorPosition - mFirstPosition; if (childIndex >= 0 && childIndex < getChildCount()) { - positionSelector(INVALID_POSITION, getChildAt(childIndex)); + positionSelector(mSelectorPosition, getChildAt(childIndex)); + selectorOnScreen = true; } - } else { + } + if (!selectorOnScreen) { mSelectorRect.setEmpty(); } diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java index 83b7d2f948f8..8fd027966e91 100644 --- a/core/java/com/android/internal/content/NativeLibraryHelper.java +++ b/core/java/com/android/internal/content/NativeLibraryHelper.java @@ -77,6 +77,8 @@ public class NativeLibraryHelper { final boolean multiArch; final boolean extractNativeLibs; final boolean debuggable; + final String pkgName; + final String apkDir; public static Handle create(File packageFile) throws IOException { try { @@ -88,19 +90,34 @@ public class NativeLibraryHelper { } public static Handle create(Package pkg) throws IOException { + String apkdir; + if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + apkdir = null; + } else { + apkdir = pkg.codePath; + } return create(pkg.getAllCodePaths(), (pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) != 0, (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS) != 0, - (pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0); + (pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0, pkg.packageName, apkdir); } public static Handle create(PackageLite lite) throws IOException { + String apkdir; + if (lite.codePath == null || + lite.codePath.startsWith("/system/") || + lite.codePath.startsWith("/vendor/") || + lite.codePath.startsWith("/oem/")) { + apkdir = null; + } else { + apkdir = lite.codePath; + } return create(lite.getAllCodePaths(), lite.multiArch, lite.extractNativeLibs, - lite.debuggable); + lite.debuggable, lite.packageName, apkdir); } private static Handle create(List codePaths, boolean multiArch, - boolean extractNativeLibs, boolean debuggable) throws IOException { + boolean extractNativeLibs, boolean debuggable, String pkgName, String apkdir) throws IOException { final int size = codePaths.size(); final long[] apkHandles = new long[size]; for (int i = 0; i < size; i++) { @@ -115,15 +132,17 @@ public class NativeLibraryHelper { } } - return new Handle(apkHandles, multiArch, extractNativeLibs, debuggable); + return new Handle(apkHandles, multiArch, extractNativeLibs, debuggable, pkgName, apkdir); } Handle(long[] apkHandles, boolean multiArch, boolean extractNativeLibs, - boolean debuggable) { + boolean debuggable, String pkgName, String apkdir) { this.apkHandles = apkHandles; this.multiArch = multiArch; this.extractNativeLibs = extractNativeLibs; this.debuggable = debuggable; + this.pkgName = pkgName; + this.apkDir = apkdir; mGuard.open("close"); } @@ -198,7 +217,9 @@ public class NativeLibraryHelper { public static int findSupportedAbi(Handle handle, String[] supportedAbis) { int finalRes = NO_NATIVE_LIBRARIES; for (long apkHandle : handle.apkHandles) { - final int res = nativeFindSupportedAbi(apkHandle, supportedAbis, handle.debuggable); + final int res = nativeFindSupportedAbiReplace(apkHandle, supportedAbis, + handle.debuggable, handle.pkgName, handle.apkDir); + if (res == NO_NATIVE_LIBRARIES) { // No native code, keep looking through all APKs. } else if (res == INSTALL_FAILED_NO_MATCHING_ABIS) { @@ -220,8 +241,8 @@ public class NativeLibraryHelper { return finalRes; } - private native static int nativeFindSupportedAbi(long handle, String[] supportedAbis, - boolean debuggable); + private native static int nativeFindSupportedAbiReplace(long handle, String[] supportedAbis, + boolean debuggable, String pkgName, String apkdir); // Convenience method to call removeNativeBinariesFromDirLI(File) public static void removeNativeBinariesLI(String nativeLibraryPath) { diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index 66475e445efa..d67c9616b135 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -38,6 +38,8 @@ import java.util.TimeZone; import java.util.logging.LogManager; import org.apache.harmony.luni.internal.util.TimezoneGetter; +import org.android_x86.analytics.AnalyticsHelper; + /** * Main entry point for runtime initialization. Not for * public consumption. @@ -61,6 +63,12 @@ public class RuntimeInit { return Log.printlns(Log.LOG_ID_CRASH, Log.ERROR, tag, msg, tr); } + // region @android-x86-analytics + // delay 120 seconds if Analytics failed to capture exception + private static final long ANDROID_X86_ANALYTICS_FAILED_DELAY = 120 * 1000; + private static long mAnalyticsEnableTime = 0; + // endregion + /** * Logs a message when a thread encounters an uncaught exception. By * default, {@link KillApplicationHandler} will terminate this process later, @@ -105,6 +113,23 @@ public class RuntimeInit { if (mCrashing) return; mCrashing = true; + // region @android-x86-analytics + if (System.currentTimeMillis() > mAnalyticsEnableTime && + SystemProperties.getBoolean("persist.sys.apps_statistics", false)) { + try { + AnalyticsHelper.captureException( + ActivityThread.currentActivityThread().getSystemContext(), + e, + t.getName(), + ActivityThread.currentPackageName()); + } catch (Throwable ex) { + // delay some time to avoid endless loop exception + mAnalyticsEnableTime = + System.currentTimeMillis() + ANDROID_X86_ANALYTICS_FAILED_DELAY; + } + } + // endregion + // Try to end profiling. If a profiler is running at this point, and we kill the // process (below), the in-memory buffer will be lost. So try to stop, which will // flush the buffer. (This makes method trace profiling useful to debug crashes.) diff --git a/core/jni/Android.bp b/core/jni/Android.bp index c62934100540..f8a35df4f401 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -11,6 +11,7 @@ cc_library_shared { "-DEGL_EGLEXT_PROTOTYPES", "-DU_USING_ICU_NAMESPACE=0", + "-D_PRC_COMPATIBILITY_PACKAGE_", "-Wall", "-Werror", @@ -34,6 +35,7 @@ cc_library_shared { "com_android_internal_content_NativeLibraryHelper.cpp", "com_google_android_gles_jni_EGLImpl.cpp", "com_google_android_gles_jni_GLImpl.cpp", // TODO: .arm + "abipicker/ABIPicker.cpp", "android_app_Activity.cpp", "android_app_ApplicationLoaders.cpp", "android_app_NativeActivity.cpp", diff --git a/core/jni/abipicker/ABIPicker.cpp b/core/jni/abipicker/ABIPicker.cpp new file mode 100644 index 000000000000..b20f4cd2f92b --- /dev/null +++ b/core/jni/abipicker/ABIPicker.cpp @@ -0,0 +1,759 @@ +#define LOG_TAG "ABIPicker" + +#include "abipicker/ABIPicker.h" +#include "abipicker/ELFLite.h" + +#include +#include +#include + +namespace android { +#define ARR_SIZE(x) (sizeof(x)/sizeof(x[0])) + +#define SO_NAME_MAX (4096) + +#define IMPOSSIBLE_LIB_NAME "/mixed/" +#define IMPOSSIBLE_LIB_LEN (sizeof(IMPOSSIBLE_LIB_NAME)-1) + +#define ARMABI "armeabi" +#define ARMV7ABI "armeabi-v7a" +#define ARM64ABI "arm64-v8a" +#define X86ABI "x86" +#define X8664ABI "x86_64" + +#define APK_LIB "lib/" +#define APK_LIB_LEN (sizeof(APK_LIB) - 1) +//#define PICK_LOGGER //flag to debug +#ifdef PICK_LOGGER +#define P_LOG(...) ALOGI(__VA_ARGS__) +#else +#define P_LOG(...) +#endif + +#define LISTPATH "/vendor/etc/misc/" +#define OEMWHITE LISTPATH ".OEMWhiteList" +#define OEMBLACK LISTPATH ".OEMBlackList" +#define THIRDPARTY LISTPATH ".ThirdPartySO" + +// load once, hold until poweroff +static Vector thirdPartySO; +static Vector cfgWhite; +static Vector cfgBlack; +static bool thirdload = false; +static bool whiteload = false; +static bool blackload = false; + +static const char* iaRelated[] = {"intel", "intl", "atom", "x86", "x64"}; + +void freeAllString(Vector& list) { + Vector::iterator it = list.begin(); + while (it != list.end()) { + if (*it != NULL) { + P_LOG("freeAllSring %p , %s", it, *it); + free(*it); + *it = NULL; + } + it++; + } +} + +void getConfig(const char* cfgFile , Vector& cfgVec) { + int read = -1; + char *line = NULL; + size_t len = 0; + + FILE* fp = fopen(cfgFile, "r"); + if (fp == NULL) { + return; + } + + freeAllString(cfgVec); + cfgVec.clear(); + + while ((read = getline(&line, &len, fp)) != -1) { + int i = 0 , j = 0; + char *cfgline = (char*)malloc(len); + if (cfgline == NULL) { + P_LOG("malloc error"); + break; + } + for (i = 0; i < read; i++) { + if (!isspace(line[i])) { + cfgline[j++] = line[i]; + } + } + cfgline[j] = '\0'; + cfgVec.push_back(cfgline); + P_LOG("orignal %s , vec: %s ", line, cfgline); + } + free(line); + fclose(fp); +} + +bool isInOEMWhiteList(const char* pkgName) { + bool result = false; + if (pkgName == NULL) { + return result; + } + + if (!whiteload) { + getConfig(OEMWHITE, cfgWhite); + whiteload = true; + } + + Vector::iterator it = cfgWhite.begin(); + for (; it != cfgWhite.end(); it++) { + P_LOG("whitelist : %s", *it); + if (0 == fnmatch(*it, pkgName, 0)) { + ALOGI("whitelist %s by %s", pkgName, *it); + result = true; + break; + } + } + return result; +} + +bool isInOEMBlackList(const char* pkgName) { + bool result = false; + if (pkgName == NULL) { + return result; + } + + if (!blackload) { + getConfig(OEMBLACK, cfgBlack); + blackload = true; + } + + Vector::iterator it = cfgBlack.begin(); + for (; it != cfgBlack.end(); it++) { + if (0 == fnmatch(*it, pkgName, 0)) { + ALOGI("blacklist %s by %s", pkgName, *it); + result = true; + break; + } + } + return result; +} + +bool isReliableLib(Vector& libList) { + unsigned sz = libList.size(); + int len = ARR_SIZE(iaRelated); + for (unsigned i = 0; i < sz; i++) { + for (int j=0; j < len; j++) { + char* p = NULL; + if (NULL != (p = strcasestr(libList[i], iaRelated[j]))) { + int lenIA = strlen(iaRelated[j]); + if (!isalpha(*(p+lenIA))) { + if (!isalpha(*(p-1)) || (p == (libList[i] + 3))) { + return true; + } + } + } + } + } + + return false; +} + +static bool isValidELF(char* buffer) { + if (buffer[EI_MAG0] != ELFMAG0 && + buffer[EI_MAG1] != ELFMAG1 && + buffer[EI_MAG2] != ELFMAG2 && + buffer[EI_MAG3] != ELFMAG3) { + return false; + } + return true; +} + +// assume that x86 has the only machine-code 3, and x86_64 62 +static bool isMixedLib(char* libCur, char* buffer) { + bool isX86_64 = (0 == strcmp(libCur, X8664ABI)) ? true: false; + uint16_t machine_code = *((uint16_t*)(&buffer[ELF_MACHINE_OFFSET])); + bool mixed = false; + if (isX86_64) { + if (machine_code != EM_X86_64) { + mixed = true; + } + } else { + if (machine_code != EM_386) { + mixed = true; + } + } + return mixed; +} + +// compare the given string with the length, igonre upper and lower +// len must be less than the length of two string +static bool ignoreCmp(const char* str1, const char* str2, int len){ + assert (str1 != NULL && str2 != NULL); + assert ((len <= strlen(str1)) && (len <= strlen(str2))); + for (int i = 0 ; i < len; i++) { + if (str1[i] != str2[i]) { + if(isalpha(str1[i]) && isalpha(str2[i]) + && (abs((str1[i]-str2[i])) == 32)) { + continue; + } + return false; + } + } + return true; +} + +static bool isInThirdPartySOList(char* libName) { + assert (libName != NULL); + size_t libLen = strlen(libName); + size_t sz = thirdPartySO.size(); + for (size_t i = 0; i < sz; i++) { + // thirdPartySO[i] won't be NULL + assert(thirdPartySO[i] != NULL); + size_t n = strlen(thirdPartySO[i]); + // three char for ".so" + int j = libLen - 4; + // now only '-' '-' and '.'found + while((j >= 0) && (isdigit(libName[j]) || (libName[j] == '-') + || (libName[j] == '_') || (libName[j] == '.'))) { + j--; + } + // three char for "lib" and include the name with no letters + if ((j == 2) || ((size_t)j == (n+2))) { + if (ignoreCmp(libName+3, thirdPartySO[i], n)) { + P_LOG("ABIpicker libName %s,In Third", libName); + return true; + } + } + + } + return false; +} + +static void insertionSort(Vector& list) { + P_LOG("in insertionSort, list size = %d\n", list.size()); + + for (size_t i = 1; i < list.size(); i++) { + int j = i - 1; + char* x = list[i]; + P_LOG("sort 1. x=%s, i=%d, j=%d\n", x, i, j); + while (j >= 0 && (strcmp(list[j], x) > 0)) { + list.replaceAt(list[j], j + 1); + j--; + } + list.replaceAt(x, j + 1); + } +} + +// Use armRef as a reference, compare all libraries of iaRef with all +// libraries of armRef.If the two are match or iaRef is more, iaRef +// will be returned with *result and true is return value. Or else, +// *result is rawResult and false is return value +bool ABIPicker::compare(char* armRef, char* iaRef, + char* rawResult, char** result) { + bool ret = true; + *result = rawResult; + + do { + assert(armRef != NULL); + if (0 == strlen(armRef)) { + *result = strlen(iaRef) > 0 ? iaRef : rawResult; + ret = strlen(iaRef) > 0 ? true : false; + break; + } + assert(iaRef != NULL); + if (0 == strlen(iaRef)) { + *result = armRef; + ret = false; + break; + } + + Vector* iaRefList = getLibList(iaRef); + Vector* armRefList = getLibList(armRef); + if (iaRefList == NULL || armRefList == NULL) { + break; + } + + if (isReliableLib(*iaRefList)) { + *result = iaRef; + break; + } + + if (compareLibList(*iaRefList, *armRefList)) { + *result = iaRef; + break; + } + + size_t iaIsvLibCount = 0; + size_t armIsvLibCount = 0; + if (!compare3rdPartyLibList(iaRef, armRef, + &iaIsvLibCount, &armIsvLibCount)) { + *result = armRef; + ret = false; + break; + } + + if (iaIsvLibCount > 0) { + *result = iaRef; + break; + } + + *result = armRef; + ret = false; + } while (0); + + ALOGV("%s Vs. %s, return %s\n", + iaRef ? iaRef : "NULL", + armRef ? armRef : "NULL", *result); + return ret; +} + +bool ABIPicker::compareLibList(Vector& iaRefList, + Vector& armRefList) { + + unsigned iaSize = iaRefList.size(); + unsigned armSize = armRefList.size(); + if (iaSize < armSize) { + return false; + } else if (iaSize == 0 && armSize == 0) { + return true; + } + + int iaNum = 0; + int armNum = 0; + Vector::iterator itIa = iaRefList.begin(); + Vector::iterator itArm = armRefList.begin(); + bool isEqual = false; + while (itIa != iaRefList.end() && itArm != armRefList.end()) { + if ((iaSize-iaNum) < (armSize-armNum)) { + return false; + } + isEqual = false ; + char* armLibName = *itArm; + int armLen = strlen (armLibName); + armNum++; + + while (itIa != iaRefList.end() && !isEqual) { + char* iaLibName = *itIa; + iaNum++; + int iaLen = strlen (iaLibName); + if (iaLen == armLen) { + if (ignoreCmp(iaLibName, armLibName, iaLen)) { + isEqual = true; + } + } + itIa++; + } + itArm++; + } + // till the end, and the last result is equal + if (itArm == armRefList.end() && isEqual){ + return true; + } + + return false; +} + +bool ABIPicker::compare3rdPartyLibList( + char* iaRef, char* armRef, + size_t* iaIsvLibCount, size_t* armIsvLibCount) { + bool result = true; + + Vector* iaRefList = getLibList(iaRef); + Vector* armRefList = getLibList(armRef); + if (iaRefList == NULL || armRefList == NULL) { + return result; + } + + if (!thirdload) { + getConfig(THIRDPARTY, thirdPartySO); + thirdload = true; + } + + Vector* armRef3rdPartyLibList = new Vector(); + Vector::iterator itArm = armRefList->begin(); + while (itArm != armRefList->end()) { + char* armLibName = *itArm; + if (isInThirdPartySOList(armLibName)) { + armRef3rdPartyLibList->push_back(armLibName); + } else { + (*armIsvLibCount)++; + } + + itArm++; + } + + Vector::iterator itIa = iaRefList->begin(); + Vector* iaRef3rdPartyLibList = new Vector(); + while (itIa != iaRefList->end()) { + char* iaLibName = *itIa; + if (isInThirdPartySOList(iaLibName)) { + iaRef3rdPartyLibList->push_back(iaLibName); + } else { + (*iaIsvLibCount)++; + } + + itIa++; + } + result = compareLibList(*iaRef3rdPartyLibList, *armRef3rdPartyLibList); + + armRef3rdPartyLibList->clear(); + delete armRef3rdPartyLibList; + iaRef3rdPartyLibList->clear(); + delete iaRef3rdPartyLibList; + return result; +} + +char* ABIPicker::getAbiName(int abi) { + if (abi < 0 || (unsigned)abi >= mLibList->size()) { + return NULL; + } + + char* ret = NULL; + int index = 0; + Vector::iterator it = mLibList->begin(); + while (it != mLibList->end()) { + if (index == abi) { + ret = (*it)->abiName; + break; + } + + index++; + it++; + } + + return ret; +} + +int ABIPicker::getAbiIndex(const char* abiName) { + int ret = -1; + + int index = 0; + Vector::iterator it = mLibList->begin(); + while (it != mLibList->end()) { + if (0 == strcmp(abiName, (*it)->abiName)) { + ret = index; + break; + } + + index++; + it++; + } + + return ret; +} + +Vector* ABIPicker::getLibList(const char* abiName) { + Vector* ret = NULL; + Vector::iterator it = mLibList->begin(); + while (it != mLibList->end()) { + if (0 == strcmp(abiName, (*it)->abiName)) { + ret = (*it)->libNameList; + break; + } + it++; + } + P_LOG("getLibList of %s return %p\n", abiName, ret); + return ret; +} + + +bool ABIPicker::isABILibValid(const char* abiName) { + Vector* specificAbiLibList = getLibList(abiName); + return ((specificAbiLibList && specificAbiLibList->size()) > 0); +} + +bool ABIPicker::foundMixedELF(const char* abiName) { + Vector* libNameList = getLibList(abiName); + if (!libNameList) { + return false; + } + if (libNameList->size() == 0) { + return false; + } + + if (0 != strcasecmp(*(libNameList->begin()), IMPOSSIBLE_LIB_NAME)) { + return false; + } + + return true; +} + +ABIPicker::ABIPicker(const char* pkgName, Vector abiList) { + mLibList = new Vector(); + mpkgName = strdup(pkgName); + if (mpkgName == NULL) { + P_LOG("ABIPicker Construct Allocated space fails"); + } + Vector::iterator it = abiList.begin(); + while (it != abiList.end()) { + if (!(*it)) { + break; + } + + struct libInfo* tmp = (struct libInfo*)calloc(1, + sizeof(struct libInfo)); + if (tmp == NULL) { + P_LOG("ABIPicker Construct Allocated space fail %s", (*it)->c_str()); + break; + } + + snprintf(tmp->abiName, (*it)->size() + 1, "%s", (*it)->c_str()); + tmp->libNameList = new Vector(); + P_LOG("ABIPicker Construct %s , libNameList: %p", + tmp->abiName, tmp->libNameList); + mLibList->push_back(tmp); + it++; + } +} + +ABIPicker::~ABIPicker(void) { + free(mpkgName); + mpkgName = NULL; + Vector::iterator it = mLibList->begin(); + while (it != mLibList->end()) { + freeAllString(*((*it)->libNameList)); + (*it)->libNameList->clear(); + delete (*it)->libNameList; + (*it)->libNameList = NULL; + + free(*it); + *it = NULL; + it++; + } + mLibList->clear(); + delete mLibList; +} + +bool ABIPicker::buildNativeLibList(void* apkHandle) { + bool ret = false; + + if (apkHandle == NULL) { + ALOGE("apkHandle is NULL\n"); + return ret; + } + + ZipFileRO* zipFile = reinterpret_cast(apkHandle); + void* cookie = NULL; + if (!zipFile->startIteration(&cookie)) { + ALOGE("apk file is broken\n"); + return ret; + } + + ZipEntryRO next = NULL; + char* unCompBuff = NULL; + char fileName[SO_NAME_MAX + 1]; + while ((next = zipFile->nextEntry(cookie))) { + if (zipFile->getEntryFileName(next, fileName, SO_NAME_MAX)) { + ALOGE("apk file is broken, can not get entry name\n"); + ret = false; + break; + } + fileName[SO_NAME_MAX] = '\0'; + + // Make sure we're in the lib directory of the ZIP. + // find out entries with such names: "lib/xxxxxxx" or "lib/" + if (strncmp(fileName, APK_LIB, APK_LIB_LEN)) { + continue; + } + + // find out any invalid ELF file + uint32_t unCompLen = 0; + if (!zipFile->getEntryInfo(next, NULL, &unCompLen, NULL, NULL, NULL, + NULL)) { + ALOGE("apk file is broken, can not get entry info\n"); + ret = false; + break; + } + + if (unCompLen == 0) { + ALOGV("skip a empty file(%s)\n", fileName); + continue; + } + + free(unCompBuff); + unCompBuff = NULL; + + unCompBuff = (char*)malloc(unCompLen); + if (unCompBuff == NULL) { + ALOGE("malloc failed size %d\n", unCompLen); + ret = false; + break; + } + memset(unCompBuff, 0, unCompLen); + // THE MOST TIME COST OPERATION + if (!zipFile->uncompressEntry(next, unCompBuff, unCompLen)) { + ALOGE("%s: uncompress failed\n", fileName); + ret = false; + break; + } + + if (!isValidELF(unCompBuff)) { + ALOGI("skip a fake .ELF file(%s)\n", fileName); + continue; + } + + // It is a real .so file, prepare to record + // find abi name and focus on what we care: arm(s) and x86(s) + // at least lastSlash points to the end of "lib/" + const char* lastSlash = strrchr(fileName, '/'); + const char* cpuAbiOffset = fileName + APK_LIB_LEN; + // just in case if fileName is in an abnormal format, like lib/libname, + // lib//libname + if (lastSlash <= cpuAbiOffset) { + ALOGI("skip a invalid lib file(%s)\n", fileName); + continue; + } + + const size_t cpuAbiRegionSize = lastSlash - cpuAbiOffset; + char curAbiName[ABI_NAME_MAX_LENGTH]; + if (cpuAbiRegionSize >= ABI_NAME_MAX_LENGTH) { + continue; + } + snprintf(curAbiName, cpuAbiRegionSize + 1, "%s", cpuAbiOffset); + + Vector* libListOfCurAbi = getLibList(curAbiName); + if (!libListOfCurAbi) { + P_LOG("getLibList of %s return NULL\n", curAbiName); + continue; + } + + // mixed arm elf in lib/x86 or lib/x86_64 + // but we don't consider a compareable scenario in lib/arm* + if (0 == strcmp(curAbiName, X86ABI) || + 0 == strcmp(curAbiName, X8664ABI)) { + if (!libListOfCurAbi->empty()) { + char* firstElement = libListOfCurAbi->itemAt(0); + if (0 == strcmp(firstElement, IMPOSSIBLE_LIB_NAME)) { + // won't add any new into the list if found mixed + // lib before + P_LOG("won't count count if found mixed lib before"); + continue; + } + } + + if (isMixedLib(curAbiName, unCompBuff)) { + P_LOG("found mixed lib(%s) in lib/%s/", curAbiName, fileName); + freeAllString(*libListOfCurAbi); + libListOfCurAbi->clear(); + char* mixedLib = (char*)malloc(IMPOSSIBLE_LIB_LEN+1); + if (!mixedLib) { + ALOGE("malloc failed size %zu", IMPOSSIBLE_LIB_LEN + 1); + ret = false; + break; + } + snprintf(mixedLib, (IMPOSSIBLE_LIB_LEN+1), "%s", IMPOSSIBLE_LIB_NAME); + mixedLib[IMPOSSIBLE_LIB_LEN] ='\0'; + libListOfCurAbi->push_back(mixedLib); + continue; + } + } + + // now, lastSlash should point to lib name + lastSlash++; + const size_t libNameSize = strlen(lastSlash); + char* curLibName = (char*)malloc(libNameSize+1); + if (!curLibName) { + ALOGE("malloc failed size %zu\n", libNameSize+1); + ret = false; + break; + } + snprintf(curLibName,libNameSize+1, "%s", lastSlash); + curLibName[libNameSize] = '\0'; + + libListOfCurAbi->push_back(curLibName); + + ret = true; + } + + free(unCompBuff); + unCompBuff = NULL; + + zipFile->endIteration(cookie); + + for (unsigned i = 0; i < mLibList->size(); i++) { + struct libInfo* tmp = mLibList->itemAt(i); + insertionSort(*(tmp->libNameList)); + } + return ret; +} + +int ABIPicker::pickupRightABI(int sysPrefer) { + char* sysPreferAbiName = getAbiName(sysPrefer); + if (!sysPreferAbiName) { + return sysPrefer; + } + + bool is64BitPrefer = (0 == strcmp(sysPreferAbiName, X8664ABI)); + bool x8664HasMixedELF = foundMixedELF(X8664ABI); + bool x86HasMixedELF = foundMixedELF(X86ABI); + + bool armv7LibValid = isABILibValid(ARMV7ABI); + bool armv5LibValid = isABILibValid(ARMABI); + bool armv8LibValid = isABILibValid(ARM64ABI); + bool x86LibValid = x86HasMixedELF ? 0 : isABILibValid(X86ABI); + bool x8664LibValid = x8664HasMixedELF ? 0 : isABILibValid(X8664ABI); + + // in OEMBlackList, need to be supported by bt + // but in case of armlib doesn't exist, we choose x86 or x86_64 + if (isInOEMBlackList(mpkgName)) { + if (armv7LibValid) { + return getAbiIndex(ARMV7ABI); + } else if (armv5LibValid) { + return getAbiIndex(ARMABI); + } else if (armv8LibValid) { + return getAbiIndex(ARM64ABI); + } + } + + char arm64Ref[ABI_NAME_MAX_LENGTH]; + if (armv8LibValid) { + snprintf(arm64Ref, sizeof(ARM64ABI), "%s", ARM64ABI); + } else { + arm64Ref[0] = '\0'; + } + + char arm32Ref[ABI_NAME_MAX_LENGTH]; + if (armv7LibValid) { + snprintf(arm32Ref, sizeof(ARMV7ABI), "%s", ARMV7ABI); + } else if (armv5LibValid) { + snprintf(arm32Ref, sizeof(ARMABI), "%s", ARMABI); + } else { + arm32Ref[0] = '\0'; + } + + char ia32Ref[ABI_NAME_MAX_LENGTH]; + if (x86LibValid) { + snprintf(ia32Ref, sizeof(X86ABI), "%s", X86ABI); + } else { + ia32Ref[0] = '\0'; + } + + char ia64Ref[ABI_NAME_MAX_LENGTH]; + if (x8664LibValid) { + snprintf(ia64Ref, ABI_NAME_MAX_LENGTH, "%s", X8664ABI); + } else { + ia64Ref[0] = '\0'; + } + + char* retAbiName = sysPreferAbiName; + do { + // # The basic rule is: + // - on 32 bit system, compare ia32Ref native libraries with + // arm32Ref native libraries. If pass, return ia32Ref . + // If fail, return arm32Ref. + // - on 64 bit system, IA has two chances. if ia64Ref native + // libraries can't pass the comparation with arm64Ref, we should + // run the comparation again with ia32Ref + if (is64BitPrefer) { + if (!compare(arm64Ref, ia64Ref, sysPreferAbiName, &retAbiName)) { + char rawRes[ABI_NAME_MAX_LENGTH]; + snprintf(rawRes, ABI_NAME_MAX_LENGTH, "%s", retAbiName); + compare(arm32Ref, ia32Ref, rawRes, &retAbiName); + } + } else { + compare(arm32Ref, ia32Ref, sysPreferAbiName, &retAbiName); + } + } while (0); + int ret = getAbiIndex(retAbiName); + ALOGI("selected abi %s(%d) for %s", retAbiName, ret, mpkgName); + return ret; +} + +} // namespace android diff --git a/core/jni/abipicker/ABIPicker.h b/core/jni/abipicker/ABIPicker.h new file mode 100644 index 000000000000..0b4a79f424d5 --- /dev/null +++ b/core/jni/abipicker/ABIPicker.h @@ -0,0 +1,44 @@ +#ifndef _ABIPICKER_H_ +#define _ABIPICKER_H_ + +#include +#include +#include +#include + +#include + +namespace android { +// assumption: the length of name of any abi type in abi list, +// like armeabi-v7a, armeabi, x86, is not longer than 64 +#define ABI_NAME_MAX_LENGTH (64) + +class ABIPicker { + public: + explicit ABIPicker(const char* pkgName,Vector abiList); + ~ABIPicker(void); + + bool buildNativeLibList(void* apkHandle); + int pickupRightABI(int sysPrefer); + private: + struct libInfo{ + char abiName[ABI_NAME_MAX_LENGTH]; + Vector* libNameList; + }; + Vector* mLibList; + char* mpkgName; + + bool foundMixedELF(const char* abiName); + bool compare(char* armRef, char* iaRef, char* rawResult, char** result); + bool compareLibList(Vector& iaRefList, Vector& armRefList); + bool compare3rdPartyLibList( char* iaRef, char* armRef, + size_t* iaIsvLibCount, size_t* armIsvLibCount); + char* getAbiName(int abi); + int getAbiIndex(const char* abiName); + bool isABILibValid(const char* abiName); + Vector* getLibList(const char* abiName); +}; + +bool isInOEMWhiteList(const char* pkgName); +} // namespace android +#endif // _ABIPICKER_H_ diff --git a/core/jni/abipicker/ELFLite.h b/core/jni/abipicker/ELFLite.h new file mode 100644 index 000000000000..e609e8ac4595 --- /dev/null +++ b/core/jni/abipicker/ELFLite.h @@ -0,0 +1,58 @@ +#ifndef _ELFLITE_H__ +#define _ELFLITE_H__ + +#include +#include +#include +#include +#include + +namespace android { +#define EI_NIDENT 16 /* Size of e_ident[] */ + +/* + * Fields in e_ident[] + */ +#define EI_MAG0 0 /* File identification byte 0 index */ +#define ELFMAG0 0x7F /* Magic number byte 0 */ +#define EI_MAG1 1 /* File identification byte 1 index */ +#define ELFMAG1 'E' /* Magic number byte 1 */ +#define EI_MAG2 2 /* File identification byte 2 index */ +#define ELFMAG2 'L' /* Magic number byte 2 */ +#define EI_MAG3 3 /* File identification byte 3 index */ +#define ELFMAG3 'F' /* Magic number byte 3 */ + +/* + * according to implementations of ELF Header + * unsigned char e_ident[16]; // ELF "magic number" + * unsigned char e_type[2]; // Identifies object file type + * unsigned char e_machine[2]; // Specifies required architecture + */ +#define ELF_MACHINE_OFFSET 18 + +/* + * Values for e_machine, which identifies the architecture. These numbers + * are officially assigned by registry@sco.com. See below for a list of + * ad-hoc numbers used during initial development. + * Please always sync them. + */ +#define EM_386 3 /* Intel 80386 */ +#define EM_486 6 /* Intel 80486 */ +#define EM_860 7 /* Intel 80860 */ +#define EM_960 19 /* Intel 80960 */ +#define EM_ARM 40 /* ARM */ +#define EM_IA_64 50 /* Intel IA-64 Processor */ +#define EM_X86_64 62 /* AMD x86-64 architecture */ +#define EM_8051 165 /* Intel 8051 and variants */ +#define EM_L1OM 180 /* Intel L1OM */ +#define EM_K1OM 181 /* Intel K1OM */ +#define EM_INTEL182 182 /* Reserved by Intel */ +#define EM_AARCH64 183 /* ARM 64-bit architecture */ +#define EM_ARM184 184 /* Reserved by ARM */ +#define EM_INTEL205 205 /* Reserved by Intel */ +#define EM_INTEL206 206 /* Reserved by Intel */ +#define EM_INTEL207 207 /* Reserved by Intel */ +#define EM_INTEL208 208 /* Reserved by Intel */ +#define EM_INTEL209 209 /* Reserved by Intel */ +} // namespace android +#endif // _ELFLITE_H__ diff --git a/core/jni/android/graphics/YuvToJpegEncoder.cpp b/core/jni/android/graphics/YuvToJpegEncoder.cpp index 31567f727a99..c1430d6cc5e4 100644 --- a/core/jni/android/graphics/YuvToJpegEncoder.cpp +++ b/core/jni/android/graphics/YuvToJpegEncoder.cpp @@ -45,6 +45,7 @@ bool YuvToJpegEncoder::encode(SkWStream* stream, void* inYuv, int width, compress(&cinfo, (uint8_t*) inYuv, offsets); jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); return true; } @@ -81,13 +82,19 @@ void Yuv420SpToJpegEncoder::compress(jpeg_compress_struct* cinfo, planes[1] = cb; planes[2] = cr; - int width = cinfo->image_width; - int height = cinfo->image_height; + JDIMENSION width = cinfo->image_width; + JDIMENSION height = cinfo->image_height; uint8_t* yPlanar = yuv + offsets[0]; uint8_t* vuPlanar = yuv + offsets[1]; //width * height; - uint8_t* uRows = new uint8_t [8 * (width >> 1)]; - uint8_t* vRows = new uint8_t [8 * (width >> 1)]; - + uint8_t* uRows = new uint8_t [8 * (((width + 15) & ~15) >> 1)]; + uint8_t* vRows = new uint8_t [8 * (((width + 15) & ~15) >> 1)]; + uint8_t* yRows; + int lastLines; + + if ((height & 0xf) != 0) { + lastLines = height & 0xf; + yRows = new uint8_t [16 * ((width + 15) & ~15)]; + } // process 16 lines of Y and 8 lines of U/V each time. while (cinfo->next_scanline < cinfo->image_height) { @@ -96,8 +103,11 @@ void Yuv420SpToJpegEncoder::compress(jpeg_compress_struct* cinfo, // Jpeg library ignores the rows whose indices are greater than height. for (int i = 0; i < 16; i++) { - // y row - y[i] = yPlanar + (cinfo->next_scanline + i) * fStrides[0]; + // y row. Add padding if height isn't aligned to 16 pixels. + if ((height & 0xf) != 0 && (cinfo->next_scanline + i) > height) + y[i] = &yRows[(i - lastLines) * ((width + 15) & ~15)]; + else + y[i] = yPlanar + (cinfo->next_scanline + i) * fStrides[0]; // construct u row and v row if ((i & 1) == 0) { @@ -109,6 +119,8 @@ void Yuv420SpToJpegEncoder::compress(jpeg_compress_struct* cinfo, } jpeg_write_raw_data(cinfo, planes, 16); } + if ((height & 0xf) != 0) + delete [] yRows; delete [] uRows; delete [] vRows; @@ -116,9 +128,12 @@ void Yuv420SpToJpegEncoder::compress(jpeg_compress_struct* cinfo, void Yuv420SpToJpegEncoder::deinterleave(uint8_t* vuPlanar, uint8_t* uRows, uint8_t* vRows, int rowIndex, int width, int height) { - int numRows = (height - rowIndex) / 2; - if (numRows > 8) numRows = 8; - for (int row = 0; row < numRows; ++row) { + int lines = 16; + //In case there isn't enough lines to process + if ((rowIndex + lines) > height) + lines = (height - rowIndex); + + for (int row = 0; row < (lines >> 1); ++row) { int offset = ((rowIndex >> 1) + row) * fStrides[1]; uint8_t* vu = vuPlanar + offset; for (int i = 0; i < (width >> 1); ++i) { diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp index fce5dd58d7f9..8f6c1597a857 100644 --- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp +++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp @@ -38,6 +38,10 @@ #include +#ifdef _PRC_COMPATIBILITY_PACKAGE_ +#include "abipicker/ABIPicker.h" +#endif + #define APK_LIB "lib/" #define APK_LIB_LEN (sizeof(APK_LIB) - 1) @@ -52,6 +56,11 @@ #define TMP_FILE_PATTERN "/tmp.XXXXXX" #define TMP_FILE_PATTERN_LEN (sizeof(TMP_FILE_PATTERN) - 1) +#ifdef _PRC_COMPATIBILITY_PACKAGE_ +#define X86ABI "x86" +#define X8664ABI "x86_64" +#endif + namespace android { // These match PackageManager.java install codes @@ -509,10 +518,102 @@ com_android_internal_content_NativeLibraryHelper_sumNativeBinaries(JNIEnv *env, } static jint -com_android_internal_content_NativeLibraryHelper_findSupportedAbi(JNIEnv *env, jclass clazz, - jlong apkHandle, jobjectArray javaCpuAbisToSearch, jboolean debuggable) +com_android_internal_content_NativeLibraryHelper_findSupportedAbi_replace( + JNIEnv *env, + jclass clazz, + jlong apkHandle, + jobjectArray javaCpuAbisToSearch, + jboolean debuggable, + jstring apkPkgName, + jstring apkDir) { - return (jint) findSupportedAbi(env, apkHandle, javaCpuAbisToSearch, debuggable); +#ifdef _PRC_COMPATIBILITY_PACKAGE_ + + int abiType = findSupportedAbi(env, apkHandle, javaCpuAbisToSearch, debuggable); + if (apkDir == NULL) { + return (jint)abiType; + } + + char abiFlag[256] = {'\0'}; + ScopedUtfChars apkdir(env, apkDir); + size_t apkdir_size = apkdir.size(); + const int numAbis = env->GetArrayLength(javaCpuAbisToSearch); + Vector supportedAbis; + + assert(apkdir_size < 256 - 15); + if (strlcpy(abiFlag, apkdir.c_str(), 256) != apkdir.size()) { + return (jint)abiType; + } + + int abiIndex = 0; + abiFlag[apkdir_size] = '/'; + abiFlag[apkdir_size + 1] = '.'; + for (abiIndex = 0; abiIndex < numAbis; abiIndex++) { + ScopedUtfChars* abiName = new ScopedUtfChars(env, + (jstring)env->GetObjectArrayElement(javaCpuAbisToSearch, abiIndex)); + supportedAbis.push_back(abiName); + if (abiName == NULL || abiName->c_str() == NULL || abiName->size() <= 0) { + break; + } + if ((strlcpy(abiFlag + apkdir_size + 2, abiName->c_str(), 256 - apkdir_size - 2) + == abiName->size()) && (access(abiFlag, F_OK) == 0)) { + abiType = abiIndex; + break; + } + } + + if (abiIndex < numAbis) { + for (int j = 0; j < abiIndex; ++j) { + if (supportedAbis[j] != NULL) { + delete supportedAbis[j]; + } + } + return (jint)abiType; + } + + do { + if (abiType < 0 || abiType >= numAbis) { + break; + } + + if (0 != strcmp(supportedAbis[abiType]->c_str(), X86ABI) && + 0 != strcmp(supportedAbis[abiType]->c_str(), X8664ABI)) { + break; + } + + ScopedUtfChars name(env, apkPkgName); + if (NULL == name.c_str()) { + break; + } + + if (isInOEMWhiteList(name.c_str())) { + break; + } + + ABIPicker picker(name.c_str(),supportedAbis); + if (!picker.buildNativeLibList((void*)apkHandle)) { + break; + } + + abiType = picker.pickupRightABI(abiType); + if (abiType >= 0 && abiType < numAbis && + (strlcpy(abiFlag + apkdir_size + 2, supportedAbis[abiType]->c_str(), + 256 - apkdir_size - 2) == supportedAbis[abiType]->size())) { + int flagFp = creat(abiFlag, 0644); + if (flagFp != -1) { + close(flagFp); + } + } + + } while(0); + + for (int i = 0; i < numAbis; ++i) { + delete supportedAbis[i]; + } + return (jint)abiType; +#else + return (jint)findSupportedAbi(env, apkHandle, javaCpuAbisToSearch, debuggable); +#endif } enum bitcode_scan_result_t { @@ -576,9 +677,9 @@ static const JNINativeMethod gMethods[] = { {"nativeSumNativeBinaries", "(JLjava/lang/String;Z)J", (void *)com_android_internal_content_NativeLibraryHelper_sumNativeBinaries}, - {"nativeFindSupportedAbi", - "(J[Ljava/lang/String;Z)I", - (void *)com_android_internal_content_NativeLibraryHelper_findSupportedAbi}, + {"nativeFindSupportedAbiReplace", + "(J[Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;)I", + (void *)com_android_internal_content_NativeLibraryHelper_findSupportedAbi_replace}, {"hasRenderscriptBitcode", "(J)I", (void *)com_android_internal_content_NativeLibraryHelper_hasRenderscriptBitcode}, }; diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp index 956b7249660f..5bddda700e2e 100644 --- a/core/jni/fd_utils.cpp +++ b/core/jni/fd_utils.cpp @@ -165,6 +165,8 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd) { const std::string fd_path = android::base::StringPrintf("/proc/self/fd/%d", fd); if (!android::base::Readlink(fd_path, &file_path)) { return NULL; + } else if (!strncmp(file_path.c_str(), "/android/", 9)) { + file_path = file_path.substr(8); } if (!whitelist->IsAllowed(file_path)) { diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 0c4352344137..78e1c4689df6 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -1366,6 +1366,8 @@ "%s SD 卡" "U 盘" "%s U 盘" + "CD/DVD 光盘" + "%s CD/DVD 光盘" "USB存储器" "修改" "流量消耗提醒" diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index 2b2ea49a7dbf..0373ec63cb4e 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -1366,6 +1366,8 @@ "%s SD 卡" "USB 驅動器" "%s USB 驅動器" + "CD/DVD 光碟機" + "%s CD/DVD 光碟機" "USB 儲存裝置" "編輯" "數據用量警告" diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 4ff84e7bb1c7..feb490167379 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -1214,7 +1214,7 @@ "請先卸載「%s」,再將其移除,以免資料遺失。" "已移除「%s」" "已移除「%s」;請插入新的媒體" - "依然退出「%s」..." + "正在退出「%s」..." "請勿移除" "設定" "退出" @@ -1366,6 +1366,8 @@ "%s SD 卡" "USB 隨身碟" "%s USB 隨身碟" + "CD/DVD 光碟機" + "%s CD/DVD 光碟機" "USB 儲存裝置" "編輯" "數據用量警告" diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index ce6815f10b30..a59b4322d3d2 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -3634,6 +3634,11 @@ %s USB drive + + CD/DVD drive + + %s CD/DVD drive + USB storage diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 15a353ba2b05..58bb175d58d5 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2426,6 +2426,8 @@ + + diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl index 2a10bdd46d07..79b6c8524292 100644 --- a/data/keyboards/Generic.kl +++ b/data/keyboards/Generic.kl @@ -139,13 +139,13 @@ key 116 POWER key 117 NUMPAD_EQUALS # key 118 "KEY_KPPLUSMINUS" key 119 BREAK -# key 120 (undefined) +key 120 APP_SWITCH key 121 NUMPAD_COMMA key 122 KANA key 123 EISU key 124 YEN -key 125 META_LEFT -key 126 META_RIGHT +key 125 HOME +key 126 HOME key 127 MENU key 128 MEDIA_STOP # key 129 "KEY_AGAIN" diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index 16d77364942e..bd4708da562a 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -223,9 +223,9 @@ void EglManager::loadConfigs() { numConfigs = 1; if (!eglChooseConfig(mEglDisplay, attribs16F, &mEglConfigWideGamut, numConfigs, &numConfigs) || numConfigs != 1) { - LOG_ALWAYS_FATAL( - "Device claims wide gamut support, cannot find matching config, error = %s", + ALOGE("Device claims wide gamut support, cannot find matching config, error = %s", eglErrorString()); + EglExtensions.pixelFormatFloat = false; } } } diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java index fc4d15fad5ca..f2a71106481b 100644 --- a/media/java/android/media/MediaFile.java +++ b/media/java/android/media/MediaFile.java @@ -43,13 +43,19 @@ public class MediaFile { public static final int FILE_TYPE_AAC = 8; public static final int FILE_TYPE_MKA = 9; public static final int FILE_TYPE_FLAC = 10; + public static final int FILE_TYPE_3GPA = 11; + public static final int FILE_TYPE_AC3 = 12; + public static final int FILE_TYPE_APE = 13; + public static final int FILE_TYPE_WEBMA = 14; + public static final int FILE_TYPE_PCM = 15; + public static final int FILE_TYPE_DTS = 16; private static final int FIRST_AUDIO_FILE_TYPE = FILE_TYPE_MP3; - private static final int LAST_AUDIO_FILE_TYPE = FILE_TYPE_FLAC; + private static final int LAST_AUDIO_FILE_TYPE = FILE_TYPE_DTS; // MIDI file types - public static final int FILE_TYPE_MID = 11; - public static final int FILE_TYPE_SMF = 12; - public static final int FILE_TYPE_IMY = 13; + public static final int FILE_TYPE_MID = 18; + public static final int FILE_TYPE_SMF = 19; + public static final int FILE_TYPE_IMY = 20; private static final int FIRST_MIDI_FILE_TYPE = FILE_TYPE_MID; private static final int LAST_MIDI_FILE_TYPE = FILE_TYPE_IMY; @@ -64,8 +70,12 @@ public class MediaFile { public static final int FILE_TYPE_MP2TS = 28; public static final int FILE_TYPE_AVI = 29; public static final int FILE_TYPE_WEBM = 30; + public static final int FILE_TYPE_DIVX = 31; + public static final int FILE_TYPE_FLV = 32; + public static final int FILE_TYPE_RV = 33; + public static final int FILE_TYPE_VC1 = 34; private static final int FIRST_VIDEO_FILE_TYPE = FILE_TYPE_MP4; - private static final int LAST_VIDEO_FILE_TYPE = FILE_TYPE_WEBM; + private static final int LAST_VIDEO_FILE_TYPE = FILE_TYPE_VC1; // More video file types public static final int FILE_TYPE_MP2PS = 200; @@ -74,13 +84,13 @@ public class MediaFile { private static final int LAST_VIDEO_FILE_TYPE2 = FILE_TYPE_QT; // Image file types - public static final int FILE_TYPE_JPEG = 31; - public static final int FILE_TYPE_GIF = 32; - public static final int FILE_TYPE_PNG = 33; - public static final int FILE_TYPE_BMP = 34; - public static final int FILE_TYPE_WBMP = 35; - public static final int FILE_TYPE_WEBP = 36; - public static final int FILE_TYPE_HEIF = 37; + public static final int FILE_TYPE_JPEG = 61; + public static final int FILE_TYPE_GIF = 62; + public static final int FILE_TYPE_PNG = 63; + public static final int FILE_TYPE_BMP = 64; + public static final int FILE_TYPE_WBMP = 65; + public static final int FILE_TYPE_WEBP = 66; + public static final int FILE_TYPE_HEIF = 67; private static final int FIRST_IMAGE_FILE_TYPE = FILE_TYPE_JPEG; private static final int LAST_IMAGE_FILE_TYPE = FILE_TYPE_HEIF; @@ -191,6 +201,7 @@ public class MediaFile { addFileType("MPGA", FILE_TYPE_MP3, "audio/mpeg", MtpConstants.FORMAT_MP3, false); addFileType("M4A", FILE_TYPE_M4A, "audio/mp4", MtpConstants.FORMAT_MPEG, false); addFileType("WAV", FILE_TYPE_WAV, "audio/x-wav", MtpConstants.FORMAT_WAV, true); + addFileType("WAV", FILE_TYPE_PCM, "audio/wav"); addFileType("AMR", FILE_TYPE_AMR, "audio/amr"); addFileType("AWB", FILE_TYPE_AWB, "audio/amr-wb"); if (isWMAEnabled()) { @@ -198,10 +209,13 @@ public class MediaFile { } addFileType("OGG", FILE_TYPE_OGG, "audio/ogg", MtpConstants.FORMAT_OGG, false); addFileType("OGG", FILE_TYPE_OGG, "application/ogg", MtpConstants.FORMAT_OGG, true); + addFileType("OGA", FILE_TYPE_OGG, "audio/ogg", MtpConstants.FORMAT_OGG, false); addFileType("OGA", FILE_TYPE_OGG, "application/ogg", MtpConstants.FORMAT_OGG, false); addFileType("AAC", FILE_TYPE_AAC, "audio/aac", MtpConstants.FORMAT_AAC, true); addFileType("AAC", FILE_TYPE_AAC, "audio/aac-adts", MtpConstants.FORMAT_AAC, false); addFileType("MKA", FILE_TYPE_MKA, "audio/x-matroska"); + addFileType("AC3", FILE_TYPE_AC3, "audio/ac3"); + addFileType("APE", FILE_TYPE_APE, "audio/x-ape"); addFileType("MID", FILE_TYPE_MID, "audio/midi"); addFileType("MIDI", FILE_TYPE_MID, "audio/midi"); @@ -226,7 +240,13 @@ public class MediaFile { addFileType("MKV", FILE_TYPE_MKV, "video/x-matroska"); addFileType("WEBM", FILE_TYPE_WEBM, "video/webm"); addFileType("TS", FILE_TYPE_MP2TS, "video/mp2ts"); + addFileType("MPG", FILE_TYPE_MP2TS, "video/mp2ts"); addFileType("AVI", FILE_TYPE_AVI, "video/avi"); + addFileType("DIVX", FILE_TYPE_DIVX, "video/divx"); + addFileType("FLV", FILE_TYPE_FLV, "video/x-flv"); + addFileType("RM", FILE_TYPE_RV, "video/vnd.rn-realvideo"); + addFileType("RMVB", FILE_TYPE_RV, "video/vnd.rn-realvideo"); + addFileType("VC1", FILE_TYPE_VC1, "video/vc1"); if (isWMVEnabled()) { addFileType("WMV", FILE_TYPE_WMV, "video/x-ms-wmv", MtpConstants.FORMAT_WMV, true); diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java index 0f0a7e9d1314..d9bdf429b4b8 100644 --- a/opengl/java/android/opengl/GLSurfaceView.java +++ b/opengl/java/android/opengl/GLSurfaceView.java @@ -969,7 +969,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0); if ((r == mRedSize) && (g == mGreenSize) - && (b == mBlueSize) && (a == mAlphaSize)) { + && (b == mBlueSize) && (a >= mAlphaSize)) { return config; } } diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java index 907a79e723ac..104321a023cf 100644 --- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java @@ -188,7 +188,10 @@ public class ImageWallpaper extends WallpaperService { @Override public void onDestroy() { super.onDestroy(); - mBackground = null; + if (mBackground != null) { + mBackground.recycle(); + mBackground = null; + } unloadWallpaper(true /* forgetSize */); } @@ -778,7 +781,7 @@ public class ImageWallpaper extends WallpaperService { mEglConfig = chooseEglConfig(); if (mEglConfig == null) { - throw new RuntimeException("eglConfig not initialized"); + return false; } mEglContext = createContext(mEgl, mEglDisplay, mEglConfig); diff --git a/services/core/java/com/android/server/WiredAccessoryManager.java b/services/core/java/com/android/server/WiredAccessoryManager.java index fcda83d30f1c..3980bc6b57d9 100644 --- a/services/core/java/com/android/server/WiredAccessoryManager.java +++ b/services/core/java/com/android/server/WiredAccessoryManager.java @@ -34,9 +34,11 @@ import com.android.server.input.InputManagerService.WiredAccessoryCallbacks; import static com.android.server.input.InputManagerService.SW_HEADPHONE_INSERT; import static com.android.server.input.InputManagerService.SW_MICROPHONE_INSERT; import static com.android.server.input.InputManagerService.SW_LINEOUT_INSERT; +import static com.android.server.input.InputManagerService.SW_VIDEOOUT_INSERT; import static com.android.server.input.InputManagerService.SW_HEADPHONE_INSERT_BIT; import static com.android.server.input.InputManagerService.SW_MICROPHONE_INSERT_BIT; import static com.android.server.input.InputManagerService.SW_LINEOUT_INSERT_BIT; +import static com.android.server.input.InputManagerService.SW_VIDEOOUT_INSERT_BIT; import java.io.File; import java.io.FileReader; @@ -111,8 +113,11 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_LINEOUT_INSERT) == 1) { switchValues |= SW_LINEOUT_INSERT_BIT; } + if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_VIDEOOUT_INSERT) == 1) { + switchValues |= SW_VIDEOOUT_INSERT_BIT; + } notifyWiredAccessoryChanged(0, switchValues, - SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_LINEOUT_INSERT_BIT); + SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_LINEOUT_INSERT_BIT | SW_VIDEOOUT_INSERT_BIT); } mObserver.init(); @@ -120,7 +125,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { @Override public void notifyWiredAccessoryChanged(long whenNanos, int switchValues, int switchMask) { - if (LOG) Slog.v(TAG, "notifyWiredAccessoryChanged: when=" + whenNanos + if (LOG) Slog.i(TAG, "notifyWiredAccessoryChanged: when=" + whenNanos + " bits=" + switchCodeToString(switchValues, switchMask) + " mask=" + Integer.toHexString(switchMask)); @@ -128,7 +133,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { int headset; mSwitchValues = (mSwitchValues & ~switchMask) | switchValues; switch (mSwitchValues & - (SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_LINEOUT_INSERT_BIT)) { + (SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_LINEOUT_INSERT_BIT | SW_VIDEOOUT_INSERT_BIT)) { case 0: headset = 0; break; @@ -141,6 +146,11 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { headset = BIT_LINEOUT; break; + case SW_VIDEOOUT_INSERT_BIT: + case SW_VIDEOOUT_INSERT_BIT | SW_LINEOUT_INSERT_BIT: + headset = BIT_HDMI_AUDIO; + break; + case SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT: headset = BIT_HEADSET; break; @@ -155,7 +165,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { } updateLocked(NAME_H2W, - (mHeadsetState & ~(BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT)) | headset); + (mHeadsetState & ~(BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT | BIT_HDMI_AUDIO)) | headset); } } @@ -285,7 +295,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { } if (LOG) { - Slog.v(TAG, "headsetName: " + headsetName + + Slog.i(TAG, "headsetName: " + headsetName + (state == 1 ? " connected" : " disconnected")); } @@ -308,6 +318,14 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { (switchValues & SW_MICROPHONE_INSERT_BIT) != 0) { sb.append("SW_MICROPHONE_INSERT"); } + if ((switchMask & SW_LINEOUT_INSERT_BIT) != 0 && + (switchValues & SW_LINEOUT_INSERT_BIT) != 0) { + sb.append("SW_LINEOUT_INSERT "); + } + if ((switchMask & SW_VIDEOOUT_INSERT_BIT) != 0 && + (switchValues & SW_VIDEOOUT_INSERT_BIT) != 0) { + sb.append("SW_VIDEOOUT_INSERT "); + } return sb.toString(); } diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index d0a1d9e63141..093b4c47a061 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -467,7 +467,9 @@ public final class DisplayManagerService extends SystemService { + ", brightness=" + brightness + ")"); mGlobalDisplayState = state; mGlobalDisplayBrightness = brightness; - applyGlobalDisplayStateLocked(mTempDisplayStateWorkQueue); + if (state != Display.STATE_OFF) { + applyGlobalDisplayStateLocked(mTempDisplayStateWorkQueue); + } } // Setting the display power state can take hundreds of milliseconds diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index fa9b1078e8ad..af96ad3972b5 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -71,6 +71,7 @@ import android.hardware.input.TouchCalibration; import android.os.Binder; import android.os.Bundle; import android.os.Environment; +import android.os.FileObserver; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -294,6 +295,9 @@ public class InputManagerService extends IInputManager.Stub /** Switch code: Headphone/Microphone Jack. When set, something is inserted. */ public static final int SW_JACK_PHYSICAL_INSERT = 0x07; + /** Switch code: Video out jack. When set, something is inserted. */ + public static final int SW_VIDEOOUT_INSERT = 0x08; + /** Switch code: Camera lens cover. When set the lens is covered. */ public static final int SW_CAMERA_LENS_COVER = 0x09; @@ -309,13 +313,27 @@ public class InputManagerService extends IInputManager.Stub public static final int SW_MICROPHONE_INSERT_BIT = 1 << SW_MICROPHONE_INSERT; public static final int SW_LINEOUT_INSERT_BIT = 1 << SW_LINEOUT_INSERT; public static final int SW_JACK_PHYSICAL_INSERT_BIT = 1 << SW_JACK_PHYSICAL_INSERT; + public static final int SW_VIDEOOUT_INSERT_BIT = 1 << SW_VIDEOOUT_INSERT; public static final int SW_JACK_BITS = - SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_JACK_PHYSICAL_INSERT_BIT | SW_LINEOUT_INSERT_BIT; + SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_JACK_PHYSICAL_INSERT_BIT | SW_LINEOUT_INSERT_BIT | SW_VIDEOOUT_INSERT_BIT; public static final int SW_CAMERA_LENS_COVER_BIT = 1 << SW_CAMERA_LENS_COVER; /** Whether to use the dev/input/event or uevent subsystem for the audio jack. */ final boolean mUseDevInputEventForAudioJack; + private class TscalObserver extends FileObserver { + public TscalObserver() { + super("/data/misc/tscal/pointercal", CLOSE_WRITE); + } + + @Override + public void onEvent(int event, String path) { + Slog.i(TAG, "detect pointercal changed"); + reloadDeviceAliases(); + } + } + private final TscalObserver mTscalObserver = new TscalObserver(); + public InputManagerService(Context context) { this.mContext = context; this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper()); @@ -332,6 +350,7 @@ public class InputManagerService extends IInputManager.Stub new File(doubleTouchGestureEnablePath); LocalServices.addService(InputManagerInternal.class, new LocalService()); + mTscalObserver.startWatching(); } public void setWindowManagerCallbacks(WindowManagerCallbacks callbacks) { diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index da14c360f16a..ba31a773d977 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -2261,7 +2261,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Only force the default orientation if the screen is xlarge, at least 960dp x 720dp, per // http://developer.android.com/guide/practices/screens_support.html#range - mForceDefaultOrientation = longSizeDp >= 960 && shortSizeDp >= 720 && + mForceDefaultOrientation = /* longSizeDp >= 960 && shortSizeDp >= 720 && */ res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation) && // For debug purposes the next line turns this feature off with: // $ adb shell setprop config.override_forced_orient true diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index ba99f38f4c38..73187d573509 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1622,12 +1622,13 @@ class DisplayContent extends WindowContainer #include #include +#include #include #include @@ -121,7 +122,7 @@ int AlarmImpl::set(int type, struct timespec *ts) int AlarmImpl::setTime(struct timeval *tv) { struct rtc_time rtc; - struct tm tm, *gmtime_res; + struct tm tm, *localtime_res, *gmtime_res; int fd; int res; @@ -144,11 +145,23 @@ int AlarmImpl::setTime(struct timeval *tv) return res; } - gmtime_res = gmtime_r(&tv->tv_sec, &tm); - if (!gmtime_res) { - ALOGV("gmtime_r() failed: %s\n", strerror(errno)); - res = -1; - goto done; + // @jide when persist.rtc_local_time is set to 1, we store the localtime to rtc + // + bool rtc_local_time = property_get_bool("persist.rtc_local_time", false); + if (rtc_local_time) { + localtime_res = localtime_r(&tv->tv_sec, &tm); + if (!localtime_res) { + ALOGV("localtime_r() failed: %s\n", strerror(errno)); + res = -1; + goto done; + } + } else { + gmtime_res = gmtime_r(&tv->tv_sec, &tm); + if (!gmtime_res) { + ALOGV("gmtime_r() failed: %s\n", strerror(errno)); + res = -1; + goto done; + } } memset(&rtc, 0, sizeof(rtc));