diff --git a/Android.mk b/Android.mk index a7eb03d0692a5..5d957d08ccdaf 100644 --- a/Android.mk +++ b/Android.mk @@ -210,6 +210,7 @@ LOCAL_SRC_FILES += \ core/java/android/service/dreams/IDozeHardware.aidl \ core/java/android/service/dreams/IDreamManager.aidl \ core/java/android/service/dreams/IDreamService.aidl \ + core/java/android/service/persistentdata/IPersistentDataBlockService.aidl \ core/java/android/service/fingerprint/IFingerprintService.aidl \ core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl \ core/java/android/service/trust/ITrustAgentService.aidl \ diff --git a/api/current.txt b/api/current.txt index e8e4e0c44fd21..80c4698f1d82e 100644 --- a/api/current.txt +++ b/api/current.txt @@ -47723,7 +47723,6 @@ package java.util { ctor public Locale(java.lang.String, java.lang.String); ctor public Locale(java.lang.String, java.lang.String, java.lang.String); method public java.lang.Object clone(); - method public static java.util.Locale forLanguageTag(java.lang.String); method public static java.util.Locale[] getAvailableLocales(); method public java.lang.String getCountry(); method public static java.util.Locale getDefault(); @@ -47733,24 +47732,15 @@ package java.util { method public java.lang.String getDisplayLanguage(java.util.Locale); method public final java.lang.String getDisplayName(); method public java.lang.String getDisplayName(java.util.Locale); - method public java.lang.String getDisplayScript(); - method public java.lang.String getDisplayScript(java.util.Locale); method public final java.lang.String getDisplayVariant(); method public java.lang.String getDisplayVariant(java.util.Locale); - method public java.lang.String getExtension(char); - method public java.util.Set getExtensionKeys(); method public java.lang.String getISO3Country(); method public java.lang.String getISO3Language(); method public static java.lang.String[] getISOCountries(); method public static java.lang.String[] getISOLanguages(); method public java.lang.String getLanguage(); - method public java.lang.String getScript(); - method public java.util.Set getUnicodeLocaleAttributes(); - method public java.util.Set getUnicodeLocaleKeys(); - method public java.lang.String getUnicodeLocaleType(java.lang.String); method public java.lang.String getVariant(); method public static synchronized void setDefault(java.util.Locale); - method public java.lang.String toLanguageTag(); method public final java.lang.String toString(); field public static final java.util.Locale CANADA; field public static final java.util.Locale CANADA_FRENCH; @@ -47768,33 +47758,14 @@ package java.util { field public static final java.util.Locale KOREA; field public static final java.util.Locale KOREAN; field public static final java.util.Locale PRC; - field public static final char PRIVATE_USE_EXTENSION = 120; // 0x0078 'x' field public static final java.util.Locale ROOT; field public static final java.util.Locale SIMPLIFIED_CHINESE; field public static final java.util.Locale TAIWAN; field public static final java.util.Locale TRADITIONAL_CHINESE; field public static final java.util.Locale UK; - field public static final char UNICODE_LOCALE_EXTENSION = 117; // 0x0075 'u' field public static final java.util.Locale US; } - public static final class Locale.Builder { - ctor public Locale.Builder(); - method public java.util.Locale.Builder addUnicodeLocaleAttribute(java.lang.String); - method public java.util.Locale build(); - method public java.util.Locale.Builder clear(); - method public java.util.Locale.Builder clearExtensions(); - method public java.util.Locale.Builder removeUnicodeLocaleAttribute(java.lang.String); - method public java.util.Locale.Builder setExtension(char, java.lang.String); - method public java.util.Locale.Builder setLanguage(java.lang.String); - method public java.util.Locale.Builder setLanguageTag(java.lang.String); - method public java.util.Locale.Builder setLocale(java.util.Locale); - method public java.util.Locale.Builder setRegion(java.lang.String); - method public java.util.Locale.Builder setScript(java.lang.String); - method public java.util.Locale.Builder setUnicodeLocaleKeyword(java.lang.String, java.lang.String); - method public java.util.Locale.Builder setVariant(java.lang.String); - } - public abstract interface Map { method public abstract void clear(); method public abstract boolean containsKey(java.lang.Object); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index fa2266b683a5e..cacb2dfad83a2 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -20,6 +20,8 @@ import android.net.wifi.IWifiScanner; import android.net.wifi.WifiScanner; import android.os.Build; +import android.service.persistentdata.IPersistentDataBlockService; +import android.service.persistentdata.PersistentDataBlockManager; import com.android.internal.policy.PolicyManager; import com.android.internal.util.Preconditions; @@ -733,7 +735,14 @@ class ContextImpl extends Context { public Object createService(ContextImpl ctx) { IBinder b = ServiceManager.getService(JOB_SCHEDULER_SERVICE); return new JobSchedulerImpl(IJobScheduler.Stub.asInterface(b)); - }}); + }}); + + registerService(PERSISTENT_DATA_BLOCK_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + IBinder b = ServiceManager.getService(PERSISTENT_DATA_BLOCK_SERVICE); + return new PersistentDataBlockManager( + IPersistentDataBlockService.Stub.asInterface(b)); + }}); } static ContextImpl getImpl(Context context) { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 80b6b5862f8a5..04275d84f65d3 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2800,6 +2800,16 @@ public abstract class Context { */ public static final String JOB_SCHEDULER_SERVICE = "jobscheduler"; + /** + * Use with {@link #getSystemService} to retrieve a {@link + * android.service.persistentdata.PersistentDataBlockManager} instance retrieving + * a file descriptor for a persistent data block. + * @see #getSystemService + * @see android.service.persistentdata.PersistentDataBlockManager + * @hide + */ + public static final String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block"; + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. diff --git a/core/java/android/service/persistentdata/IPersistentDataBlockService.aidl b/core/java/android/service/persistentdata/IPersistentDataBlockService.aidl new file mode 100644 index 0000000000000..06e776d3fd7b2 --- /dev/null +++ b/core/java/android/service/persistentdata/IPersistentDataBlockService.aidl @@ -0,0 +1,20 @@ +package android.service.persistentdata; + +import android.os.ParcelFileDescriptor; + +/** + * Internal interface through which to communicate to the + * PersistentDataBlockService. The persistent data block allows writing + * raw data and setting the OEM unlock enabled/disabled bit contained + * in the partition. + * + * @hide + */ +interface IPersistentDataBlockService { + int write(in byte[] data); + int read(out byte[] data); + int getDataBlockSize(); + + void setOemUnlockEnabled(boolean enabled); + boolean getOemUnlockEnabled(); +} diff --git a/core/java/android/service/persistentdata/PersistentDataBlockManager.java b/core/java/android/service/persistentdata/PersistentDataBlockManager.java new file mode 100644 index 0000000000000..afcf71731f829 --- /dev/null +++ b/core/java/android/service/persistentdata/PersistentDataBlockManager.java @@ -0,0 +1,97 @@ +package android.service.persistentdata; + +import android.os.RemoteException; +import android.util.Slog; + +/** + * Interface for reading and writing data blocks to a persistent partition. + * + * Allows writing one block at a time. Namely, each time + * {@link android.service.persistentdata.PersistentDataBlockManager}.write(byte[] data) + * is called, it will overwite the data that was previously written on the block. + * + * Clients can query the size of the currently written block via + * {@link android.service.persistentdata.PersistentDataBlockManager}.getTotalDataSize(). + * + * Clients can any number of bytes from the currently written block up to its total size by invoking + * {@link android.service.persistentdata.PersistentDataBlockManager}.read(byte[] data). + * + * @hide + */ +public class PersistentDataBlockManager { + private static final String TAG = PersistentDataBlockManager.class.getSimpleName(); + private IPersistentDataBlockService sService; + + public PersistentDataBlockManager(IPersistentDataBlockService service) { + sService = service; + } + + /** + * Writes {@code data} to the persistent partition. Previously written data + * will be overwritten. This data will persist across factory resets. + * + * @param data the data to write + */ + public void write(byte[] data) { + try { + sService.write(data); + } catch (RemoteException e) { + onError("writing data"); + } + } + + /** + * Tries to read {@code data.length} bytes into {@code data}. Call {@code getDataBlockSize()} + * to determine the total size of the block currently residing in the persistent partition. + * + * @param data the buffer in which to read the data + * @return the actual number of bytes read + */ + public int read(byte[] data) { + try { + return sService.read(data); + } catch (RemoteException e) { + onError("reading data"); + return -1; + } + } + + /** + * Retrieves the size of the block currently written to the persistent partition. + */ + public int getDataBlockSize() { + try { + return sService.getDataBlockSize(); + } catch (RemoteException e) { + onError("getting data block size"); + return 0; + } + } + + /** + * Writes a byte enabling or disabling the ability to "OEM unlock" the device. + */ + public void setOemUnlockEnabled(boolean enabled) { + try { + sService.setOemUnlockEnabled(enabled); + } catch (RemoteException e) { + onError("setting OEM unlock enabled to " + enabled); + } + } + + /** + * Returns whether or not "OEM unlock" is enabled or disabled on this device. + */ + public boolean getOemUnlockEnabled() { + try { + return sService.getOemUnlockEnabled(); + } catch (RemoteException e) { + onError("getting OEM unlock enabled bit"); + return false; + } + } + + private void onError(String msg) { + Slog.v(TAG, "Remote exception while " + msg); + } +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 59399d2633b14..e38fce943b403 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1075,6 +1075,13 @@ + + + + diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index f34fbc13b1d00..4585b7873a520 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1610,12 +1610,15 @@ --> - + + + + true + the IMS connection --> true diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 942221274d69c..20650883f1627 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1668,6 +1668,7 @@ + diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java new file mode 100644 index 0000000000000..1eded6f23b6c4 --- /dev/null +++ b/services/core/java/com/android/server/PersistentDataBlockService.java @@ -0,0 +1,261 @@ +package com.android.server; + +import android.Manifest; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.SystemProperties; +import android.service.persistentdata.IPersistentDataBlockService; +import android.util.Log; +import com.android.internal.R; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; + +/** + * Service for reading and writing blocks to a persistent partition. + * This data will live across factory resets. + * + * Allows writing one block at a time. Namely, each time + * {@link android.service.persistentdata.IPersistentDataBlockService}.write(byte[] data) + * is called, it will overwite the data that was previously written on the block. + * + * Clients can query the size of the currently written block via + * {@link android.service.persistentdata.IPersistentDataBlockService}.getTotalDataSize(). + * + * Clients can any number of bytes from the currently written block up to its total size by invoking + * {@link android.service.persistentdata.IPersistentDataBlockService}.read(byte[] data) + */ +public class PersistentDataBlockService extends SystemService { + private static final String TAG = PersistentDataBlockService.class.getSimpleName(); + + private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst"; + private static final int HEADER_SIZE = 8; + private static final int BLOCK_ID = 0x1990; + + private final Context mContext; + private final String mDataBlockFile; + private long mBlockDeviceSize; + + private final int mAllowedUid; + + public PersistentDataBlockService(Context context) { + super(context); + mContext = context; + mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP); + mBlockDeviceSize = 0; // Load lazily + String allowedPackage = context.getResources() + .getString(R.string.config_persistentDataPackageName); + PackageManager pm = mContext.getPackageManager(); + int allowedUid = -1; + try { + allowedUid = pm.getPackageUid(allowedPackage, + Binder.getCallingUserHandle().getIdentifier()); + } catch (PackageManager.NameNotFoundException e) { + // not expected + Log.e(TAG, "not able to find package " + allowedPackage, e); + } + + mAllowedUid = allowedUid; + } + + @Override + public void onStart() { + publishBinderService(Context.PERSISTENT_DATA_BLOCK_SERVICE, mService); + } + + private void enforceOemUnlockPermission() { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.OEM_UNLOCK_STATE, + "Can't access OEM unlock state"); + } + + private void enforceUid(int callingUid) { + if (callingUid != mAllowedUid) { + throw new SecurityException("uid " + callingUid + " not allowed to access PST"); + } + } + + private int getTotalDataSize(DataInputStream inputStream) throws IOException { + int totalDataSize; + int blockId = inputStream.readInt(); + if (blockId == BLOCK_ID) { + totalDataSize = inputStream.readInt(); + } else { + totalDataSize = 0; + } + return totalDataSize; + } + + private long maybeReadBlockDeviceSize() { + synchronized (this) { + if (mBlockDeviceSize == 0) { + mBlockDeviceSize = getBlockDeviceSize(mDataBlockFile); + } + } + + return mBlockDeviceSize; + } + + private native long getBlockDeviceSize(String path); + + private final IBinder mService = new IPersistentDataBlockService.Stub() { + @Override + public int write(byte[] data) throws RemoteException { + enforceUid(Binder.getCallingUid()); + + // Need to ensure we don't write over the last byte + if (data.length > maybeReadBlockDeviceSize() - HEADER_SIZE - 1) { + return -1; + } + + DataOutputStream outputStream; + try { + outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile))); + } catch (FileNotFoundException e) { + Log.e(TAG, "partition not available?", e); + return -1; + } + + ByteBuffer headerAndData = ByteBuffer.allocate(data.length + HEADER_SIZE); + headerAndData.putInt(BLOCK_ID); + headerAndData.putInt(data.length); + headerAndData.put(data); + + try { + outputStream.write(headerAndData.array()); + return data.length; + } catch (IOException e) { + Log.e(TAG, "failed writing to the persistent data block", e); + return -1; + } finally { + try { + outputStream.close(); + } catch (IOException e) { + Log.e(TAG, "failed closing output stream", e); + } + } + } + + @Override + public int read(byte[] data) { + enforceUid(Binder.getCallingUid()); + + DataInputStream inputStream; + try { + inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile))); + } catch (FileNotFoundException e) { + Log.e(TAG, "partition not available?", e); + return -1; + } + + try { + int totalDataSize = getTotalDataSize(inputStream); + return inputStream.read(data, 0, + (data.length > totalDataSize) ? totalDataSize : data.length); + } catch (IOException e) { + Log.e(TAG, "failed to read data", e); + return -1; + } finally { + try { + inputStream.close(); + } catch (IOException e) { + Log.e(TAG, "failed to close OutputStream"); + } + } + } + + @Override + public void setOemUnlockEnabled(boolean enabled) { + enforceOemUnlockPermission(); + FileOutputStream outputStream; + try { + outputStream = new FileOutputStream(new File(mDataBlockFile)); + } catch (FileNotFoundException e) { + Log.e(TAG, "parition not available", e); + return; + } + + try { + FileChannel channel = outputStream.getChannel(); + + channel.position(maybeReadBlockDeviceSize() - 1); + + ByteBuffer data = ByteBuffer.allocate(1); + data.put(enabled ? (byte) 1 : (byte) 0); + data.flip(); + + channel.write(data); + } catch (IOException e) { + Log.e(TAG, "unable to access persistent partition", e); + } finally { + try { + outputStream.close(); + } catch (IOException e) { + Log.e(TAG, "failed to close OutputStream"); + } + } + } + + @Override + public boolean getOemUnlockEnabled() { + enforceOemUnlockPermission(); + DataInputStream inputStream; + try { + inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile))); + } catch (FileNotFoundException e) { + Log.e(TAG, "partition not available"); + return false; + } + + try { + inputStream.skip(maybeReadBlockDeviceSize() - 1); + return inputStream.readByte() != 0; + } catch (IOException e) { + Log.e(TAG, "unable to access persistent partition", e); + return false; + } finally { + try { + inputStream.close(); + } catch (IOException e) { + Log.e(TAG, "failed to close OutputStream"); + } + } + } + + @Override + public int getDataBlockSize() { + enforceUid(Binder.getCallingUid()); + + DataInputStream inputStream; + try { + inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile))); + } catch (FileNotFoundException e) { + Log.e(TAG, "partition not available"); + return 0; + } + + try { + return getTotalDataSize(inputStream); + } catch (IOException e) { + Log.e(TAG, "error reading data block size"); + return 0; + } finally { + try { + inputStream.close(); + } catch (IOException e) { + Log.e(TAG, "failed to close OutputStream"); + } + } + } + }; +} diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk index db44d3a2893e2..5599760c65966 100644 --- a/services/core/jni/Android.mk +++ b/services/core/jni/Android.mk @@ -26,6 +26,7 @@ LOCAL_SRC_FILES += \ $(LOCAL_REL_DIR)/com_android_server_UsbDeviceManager.cpp \ $(LOCAL_REL_DIR)/com_android_server_UsbHostManager.cpp \ $(LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \ + $(LOCAL_REL_DIR)/com_android_server_PersistentDataBlockService.cpp \ $(LOCAL_REL_DIR)/onload.cpp include external/stlport/libstlport.mk diff --git a/services/core/jni/com_android_server_PersistentDataBlockService.cpp b/services/core/jni/com_android_server_PersistentDataBlockService.cpp new file mode 100644 index 0000000000000..673a6e20a9c09 --- /dev/null +++ b/services/core/jni/com_android_server_PersistentDataBlockService.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace android { + + uint64_t get_block_device_size(int fd) + { + uint64_t size = 0; + int ret; + + ret = ioctl(fd, BLKGETSIZE64, &size); + + if (ret) + return 0; + + return size; + } + + static jlong com_android_server_PeristentDataBlockService_getBlockDeviceSize(JNIEnv *env, jclass, jstring jpath) { + const char *path = env->GetStringUTFChars(jpath, 0); + int fd = open(path, O_RDONLY); + + if (fd < 0) + return 0; + + return get_block_device_size(fd); + } + + static JNINativeMethod sMethods[] = { + /* name, signature, funcPtr */ + {"getBlockDeviceSize", "(Ljava/lang/String;)J", (void*)com_android_server_PeristentDataBlockService_getBlockDeviceSize}, + }; + + int register_android_server_PersistentDataBlockService(JNIEnv* env) + { + return jniRegisterNativeMethods(env, "com/android/server/PersistentDataBlockService", + sMethods, NELEM(sMethods)); + } + +} /* namespace android */ \ No newline at end of file diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index a3021044b78c3..bf7501ed53537 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -41,6 +41,7 @@ int register_android_server_dreams_McuHal(JNIEnv* env); int register_android_server_hdmi_HdmiCecController(JNIEnv* env); int register_android_server_hdmi_HdmiMhlController(JNIEnv* env); int register_android_server_tv_TvInputHal(JNIEnv* env); +int register_android_server_PersistentDataBlockService(JNIEnv* env); }; using namespace android; @@ -77,6 +78,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) register_android_server_hdmi_HdmiCecController(env); register_android_server_hdmi_HdmiMhlController(env); register_android_server_tv_TvInputHal(env); + register_android_server_PersistentDataBlockService(env); return JNI_VERSION_1_4; } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index b63bbe7fbe6c5..e0ebd5494e8f0 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -135,6 +135,7 @@ public final class SystemServer { "com.android.server.ethernet.EthernetService"; private static final String JOB_SCHEDULER_SERVICE_CLASS = "com.android.server.job.JobSchedulerService"; + private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst"; private final int mFactoryTestMode; private Timer mProfilerSnapshotTimer; @@ -574,6 +575,10 @@ public final class SystemServer { reportWtf("starting LockSettingsService service", e); } + if (!SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP).equals("")) { + mSystemServiceManager.startService(PersistentDataBlockService.class); + } + // Always start the Device Policy Manager, so that the API is compatible with // API8. mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class);