Add CMSettingsProvider and CMDatabaseHelper
issue-id: CYNGNOS-828 Change-Id: I01c08c0e432d6a941950a565e5ab6664664e2a7f
This commit is contained in:
parent
e949433c08
commit
0eb2999091
@ -163,7 +163,7 @@ LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:= build/tools/droiddoc/templates-sdk
|
||||
|
||||
LOCAL_DROIDDOC_OPTIONS:= \
|
||||
-stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/cmsdk_stubs_current_intermediates/src \
|
||||
-stubpackages cyanogenmod.alarmclock:cyanogenmod.app:cyanogenmod.hardware:cyanogenmod.os:cyanogenmod.profiles:cyanogenmod.platform:org.cyanogenmod.platform \
|
||||
-stubpackages cyanogenmod.alarmclock:cyanogenmod.app:cyanogenmod.hardware:cyanogenmod.os:cyanogenmod.profiles:cyanogenmod.providers:cyanogenmod.platform:org.cyanogenmod.platform \
|
||||
-api $(INTERNAL_CM_PLATFORM_API_FILE) \
|
||||
-removedApi $(INTERNAL_CM_PLATFORM_REMOVED_API_FILE) \
|
||||
-nodocs \
|
||||
@ -192,7 +192,7 @@ LOCAL_MODULE := cm-system-api-stubs
|
||||
|
||||
LOCAL_DROIDDOC_OPTIONS:=\
|
||||
-stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/cmsdk_system_stubs_current_intermediates/src \
|
||||
-stubpackages cyanogenmod.alarmclock:cyanogenmod.app:cyanogenmod.hardware:cyanogenmod.os:cyanogenmod.profiles:cyanogenmod.platform:org.cyanogenmod.platform \
|
||||
-stubpackages cyanogenmod.alarmclock:cyanogenmod.app:cyanogenmod.hardware:cyanogenmod.os:cyanogenmod.profiles:cyanogenmod.providers:cyanogenmod.platform:org.cyanogenmod.platform \
|
||||
-showAnnotation android.annotation.SystemApi \
|
||||
-api $(INTERNAL_CM_PLATFORM_SYSTEM_API_FILE) \
|
||||
-removedApi $(INTERNAL_CM_PLATFORM_SYSTEM_REMOVED_API_FILE) \
|
||||
|
@ -442,6 +442,8 @@ package cyanogenmod.platform {
|
||||
field public static final java.lang.String MODIFY_SOUND_SETTINGS = "cyanogenmod.permission.MODIFY_SOUND_SETTINGS";
|
||||
field public static final java.lang.String PUBLISH_CUSTOM_TILE = "cyanogenmod.permission.PUBLISH_CUSTOM_TILE";
|
||||
field public static final java.lang.String READ_MSIM_PHONE_STATE = "cyanogenmod.permission.READ_MSIM_PHONE_STATE";
|
||||
field public static final java.lang.String WRITE_SECURE_SETTINGS = "cyanogenmod.permission.WRITE_SECURE_SETTINGS";
|
||||
field public static final java.lang.String WRITE_SETTINGS = "cyanogenmod.permission.WRITE_SETTINGS";
|
||||
}
|
||||
|
||||
public final class R {
|
||||
@ -566,3 +568,68 @@ package cyanogenmod.profiles {
|
||||
|
||||
}
|
||||
|
||||
package cyanogenmod.providers {
|
||||
|
||||
public final class CMSettings {
|
||||
ctor public CMSettings();
|
||||
field public static final java.lang.String AUTHORITY = "cmsettings";
|
||||
}
|
||||
|
||||
public static class CMSettings.CMSettingNotFoundException extends android.util.AndroidException {
|
||||
ctor public CMSettings.CMSettingNotFoundException(java.lang.String);
|
||||
}
|
||||
|
||||
public static final class CMSettings.Global extends android.provider.Settings.NameValueTable {
|
||||
ctor public CMSettings.Global();
|
||||
method public static float getFloat(android.content.ContentResolver, java.lang.String, float);
|
||||
method public static float getFloat(android.content.ContentResolver, java.lang.String) throws cyanogenmod.providers.CMSettings.CMSettingNotFoundException;
|
||||
method public static int getInt(android.content.ContentResolver, java.lang.String, int);
|
||||
method public static int getInt(android.content.ContentResolver, java.lang.String) throws cyanogenmod.providers.CMSettings.CMSettingNotFoundException;
|
||||
method public static long getLong(android.content.ContentResolver, java.lang.String, long);
|
||||
method public static long getLong(android.content.ContentResolver, java.lang.String) throws cyanogenmod.providers.CMSettings.CMSettingNotFoundException;
|
||||
method public static java.lang.String getString(android.content.ContentResolver, java.lang.String);
|
||||
method public static boolean putFloat(android.content.ContentResolver, java.lang.String, float);
|
||||
method public static boolean putInt(android.content.ContentResolver, java.lang.String, int);
|
||||
method public static boolean putLong(android.content.ContentResolver, java.lang.String, long);
|
||||
method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String);
|
||||
field public static final android.net.Uri CONTENT_URI;
|
||||
field public static final java.lang.String SYS_PROP_CM_SETTING_VERSION = "sys.cm_settings_global_version";
|
||||
}
|
||||
|
||||
public static final class CMSettings.Secure extends android.provider.Settings.NameValueTable {
|
||||
ctor public CMSettings.Secure();
|
||||
method public static float getFloat(android.content.ContentResolver, java.lang.String, float);
|
||||
method public static float getFloat(android.content.ContentResolver, java.lang.String) throws cyanogenmod.providers.CMSettings.CMSettingNotFoundException;
|
||||
method public static int getInt(android.content.ContentResolver, java.lang.String, int);
|
||||
method public static int getInt(android.content.ContentResolver, java.lang.String) throws cyanogenmod.providers.CMSettings.CMSettingNotFoundException;
|
||||
method public static long getLong(android.content.ContentResolver, java.lang.String, long);
|
||||
method public static long getLong(android.content.ContentResolver, java.lang.String) throws cyanogenmod.providers.CMSettings.CMSettingNotFoundException;
|
||||
method public static java.lang.String getString(android.content.ContentResolver, java.lang.String);
|
||||
method public static boolean putFloat(android.content.ContentResolver, java.lang.String, float);
|
||||
method public static boolean putInt(android.content.ContentResolver, java.lang.String, int);
|
||||
method public static boolean putLong(android.content.ContentResolver, java.lang.String, long);
|
||||
method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String);
|
||||
field public static final android.net.Uri CONTENT_URI;
|
||||
field public static final java.lang.String NAME_THEME_CONFIG = "name_theme_config";
|
||||
field public static final java.lang.String SYS_PROP_CM_SETTING_VERSION = "sys.cm_settings_secure_version";
|
||||
}
|
||||
|
||||
public static final class CMSettings.System extends android.provider.Settings.NameValueTable {
|
||||
ctor public CMSettings.System();
|
||||
method public static float getFloat(android.content.ContentResolver, java.lang.String, float);
|
||||
method public static float getFloat(android.content.ContentResolver, java.lang.String) throws cyanogenmod.providers.CMSettings.CMSettingNotFoundException;
|
||||
method public static int getInt(android.content.ContentResolver, java.lang.String, int);
|
||||
method public static int getInt(android.content.ContentResolver, java.lang.String) throws cyanogenmod.providers.CMSettings.CMSettingNotFoundException;
|
||||
method public static long getLong(android.content.ContentResolver, java.lang.String, long);
|
||||
method public static long getLong(android.content.ContentResolver, java.lang.String) throws cyanogenmod.providers.CMSettings.CMSettingNotFoundException;
|
||||
method public static java.lang.String getString(android.content.ContentResolver, java.lang.String);
|
||||
method public static boolean putFloat(android.content.ContentResolver, java.lang.String, float);
|
||||
method public static boolean putInt(android.content.ContentResolver, java.lang.String, int);
|
||||
method public static boolean putLong(android.content.ContentResolver, java.lang.String, long);
|
||||
method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String);
|
||||
field public static final android.net.Uri CONTENT_URI;
|
||||
field public static final java.lang.String SYS_PROP_CM_SETTING_VERSION = "sys.cm_settings_system_version";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -69,6 +69,19 @@
|
||||
android:description="@string/permdesc_useHardwareFramework"
|
||||
android:protectionLevel="system|signature" />
|
||||
|
||||
<!-- Allows an application to write to CM system settings -->
|
||||
<permission android:name="cyanogenmod.permission.WRITE_SETTINGS"
|
||||
android:label="@string/permlab_writeSettings"
|
||||
android:description="@string/permdesc_writeSettings"
|
||||
android:protectionLevel="normal" />
|
||||
|
||||
<!-- Allows an application to write to secure CM system settings.
|
||||
<p>Not for use by third-party applications. -->
|
||||
<permission android:name="cyanogenmod.permission.WRITE_SECURE_SETTINGS"
|
||||
android:label="@string/permlab_writeSecureSettings"
|
||||
android:description="@string/permdesc_writeSecureSettings"
|
||||
android:protectionLevel="signature|system|development" />
|
||||
|
||||
<application android:process="system"
|
||||
android:persistent="true"
|
||||
android:hasCode="false"
|
||||
|
@ -42,6 +42,13 @@
|
||||
<string name="permlab_useHardwareFramework">use hardware framework</string>
|
||||
<string name="permdesc_useHardwareFramework">Allows an app access to the CM hardware framework.</string>
|
||||
|
||||
<!-- Labels for the WRITE_SETTINGS permission -->
|
||||
<string name="permlab_writeSettings">modify CM system settings</string>
|
||||
<string name="permdesc_writeSettings">Allows an app to modify CM system settings.</string>
|
||||
|
||||
<!-- Labels for the WRITE_SECURE_SETTINGS permission -->
|
||||
<string name="permlab_writeSecureSettings">modify CM secure system settings</string>
|
||||
<string name="permdesc_writeSecureSettings">Allows an app to modify CM secure system settings. Not for use by normal apps.</string>
|
||||
|
||||
<!-- Label to show for a service that is running because it is observing the user's custom tiles. -->
|
||||
<string name="custom_tile_listener_binding_label">Custom tile listener</string>
|
||||
|
36
packages/CMSettingsProvider/Android.mk
Normal file
36
packages/CMSettingsProvider/Android.mk
Normal file
@ -0,0 +1,36 @@
|
||||
#
|
||||
# Copyright (C) 2015 The CyanogenMod Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
src_dir := src
|
||||
res_dir := res
|
||||
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dir))
|
||||
LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
|
||||
|
||||
LOCAL_PACKAGE_NAME := CMSettingsProvider
|
||||
LOCAL_CERTIFICATE := platform
|
||||
LOCAL_PRIVILEGED_MODULE := true
|
||||
|
||||
LOCAL_JAVA_LIBRARIES := \
|
||||
org.cyanogenmod.platform.sdk
|
||||
|
||||
include $(BUILD_PACKAGE)
|
||||
|
||||
########################
|
||||
include $(call all-makefiles-under,$(LOCAL_PATH))
|
42
packages/CMSettingsProvider/AndroidManifest.xml
Normal file
42
packages/CMSettingsProvider/AndroidManifest.xml
Normal file
@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2015 The CyanogenMod Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.cyanogenmod.cmsettings"
|
||||
coreApp="true"
|
||||
android:sharedUserId="android.uid.system">
|
||||
<!-- It is necessary to be a system app in order to update table versions in SystemProperties for
|
||||
CMSettings to know whether or not the client side cache is up to date. It is also necessary
|
||||
to run in the system process in order to start the content provider prior to running migration
|
||||
for CM settings on user starting -->
|
||||
|
||||
<uses-permission android:name="android.permission.MANAGE_USERS" />
|
||||
|
||||
<application android:icon="@drawable/icon"
|
||||
android:label="@string/app_name"
|
||||
android:process="system"
|
||||
android:killAfterRestore="false"
|
||||
android:allowClearUserData="false"
|
||||
android:enabled="true">
|
||||
|
||||
<provider android:name="CMSettingsProvider" android:authorities="cmsettings"
|
||||
android:multiprocess="false"
|
||||
android:exported="true"
|
||||
android:writePermission="cyanogenmod.permission.WRITE_SETTINGS"
|
||||
android:singleUser="true"
|
||||
android:initOrder="100" />
|
||||
|
||||
</application>
|
||||
</manifest>
|
BIN
packages/CMSettingsProvider/res/drawable/icon.png
Normal file
BIN
packages/CMSettingsProvider/res/drawable/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 990 B |
19
packages/CMSettingsProvider/res/values/strings.xml
Normal file
19
packages/CMSettingsProvider/res/values/strings.xml
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2014-2015 The CyanogenMod 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>
|
||||
<string name="app_name">CMSettingsProvider</string>
|
||||
</resources>
|
@ -0,0 +1,126 @@
|
||||
/**
|
||||
* Copyright (c) 2015, The CyanogenMod 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 org.cyanogenmod.cmsettings;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.os.Environment;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* The CMDatabaseHelper allows creation of a database to store CM specific settings for a user
|
||||
* in System, Secure, and Global tables.
|
||||
*/
|
||||
public class CMDatabaseHelper extends SQLiteOpenHelper{
|
||||
private static final String TAG = "CMDatabaseHelper";
|
||||
private static final boolean LOCAL_LOGV = false;
|
||||
|
||||
private static final String DATABASE_NAME = "cmsettings.db";
|
||||
private static final int DATABASE_VERSION = 1;
|
||||
|
||||
static class CMTableNames {
|
||||
static final String TABLE_SYSTEM = "system";
|
||||
static final String TABLE_SECURE = "secure";
|
||||
static final String TABLE_GLOBAL = "global";
|
||||
}
|
||||
|
||||
private static final String CREATE_TABLE_SQL_FORMAT = "CREATE TABLE %s (" +
|
||||
"_id INTEGER PRIMARY KEY AUTOINCREMENT," +
|
||||
"name TEXT UNIQUE ON CONFLICT REPLACE," +
|
||||
"value TEXT" +
|
||||
");)";
|
||||
|
||||
private static final String CREATE_INDEX_SQL_FORMAT = "CREATE INDEX %sIndex%d ON %s (name);";
|
||||
|
||||
private int mUserHandle;
|
||||
|
||||
/**
|
||||
* Gets the appropriate database path for a specific user
|
||||
* @param userId The database path for this user
|
||||
* @return The database path string
|
||||
*/
|
||||
static String dbNameForUser(final int userId) {
|
||||
// The owner gets the unadorned db name;
|
||||
if (userId == UserHandle.USER_OWNER) {
|
||||
return DATABASE_NAME;
|
||||
} else {
|
||||
// Place the database in the user-specific data tree so that it's
|
||||
// cleaned up automatically when the user is deleted.
|
||||
File databaseFile = new File(
|
||||
Environment.getUserSystemDirectory(userId), DATABASE_NAME);
|
||||
return databaseFile.getPath();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of {@link CMDatabaseHelper}
|
||||
* @param context
|
||||
* @param userId
|
||||
*/
|
||||
public CMDatabaseHelper(Context context, int userId) {
|
||||
super(context, dbNameForUser(userId), null, DATABASE_VERSION);
|
||||
mUserHandle = userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates System, Secure, and Global tables in the specified {@link SQLiteDatabase}
|
||||
* @param db The database.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
db.beginTransaction();
|
||||
|
||||
try {
|
||||
createDbTable(db, CMTableNames.TABLE_SYSTEM);
|
||||
createDbTable(db, CMTableNames.TABLE_SECURE);
|
||||
|
||||
if (mUserHandle == UserHandle.USER_OWNER) {
|
||||
createDbTable(db, CMTableNames.TABLE_GLOBAL);
|
||||
}
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
|
||||
if (LOCAL_LOGV) Log.v(TAG, "Successfully created tables for cm settings db");
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a table and index for the specified database and table name
|
||||
* @param db
|
||||
* @param tableName
|
||||
*/
|
||||
private void createDbTable(SQLiteDatabase db, String tableName) {
|
||||
if (LOCAL_LOGV) Log.v(TAG, "Creating table and index for: " + tableName);
|
||||
|
||||
String createTableSql = String.format(CREATE_TABLE_SQL_FORMAT, tableName);
|
||||
db.execSQL(createTableSql);
|
||||
|
||||
String createIndexSql = String.format(CREATE_INDEX_SQL_FORMAT, tableName, 1, tableName);
|
||||
db.execSQL(createIndexSql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,451 @@
|
||||
/**
|
||||
* Copyright (c) 2015, The CyanogenMod 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 org.cyanogenmod.cmsettings;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.UriMatcher;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.AbstractCursor;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteCantOpenDatabaseException;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteQueryBuilder;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import cyanogenmod.providers.CMSettings;
|
||||
|
||||
/**
|
||||
* The CMSettingsProvider serves as a {@link ContentProvider} for CM specific settings
|
||||
*/
|
||||
public class CMSettingsProvider extends ContentProvider {
|
||||
private static final String TAG = "CMSettingsProvider";
|
||||
private static final boolean LOCAL_LOGV = false;
|
||||
|
||||
private static final boolean USER_CHECK_THROWS = true;
|
||||
|
||||
// Each defined user has their own settings
|
||||
protected final SparseArray<CMDatabaseHelper> mDbHelpers = new SparseArray<CMDatabaseHelper>();
|
||||
|
||||
private static final int SYSTEM = 1;
|
||||
private static final int SECURE = 2;
|
||||
private static final int GLOBAL = 3;
|
||||
|
||||
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||
|
||||
static {
|
||||
sUriMatcher.addURI(CMSettings.AUTHORITY, CMDatabaseHelper.CMTableNames.TABLE_SYSTEM,
|
||||
SYSTEM);
|
||||
sUriMatcher.addURI(CMSettings.AUTHORITY, CMDatabaseHelper.CMTableNames.TABLE_SECURE,
|
||||
SECURE);
|
||||
sUriMatcher.addURI(CMSettings.AUTHORITY, CMDatabaseHelper.CMTableNames.TABLE_GLOBAL,
|
||||
GLOBAL);
|
||||
// TODO add other paths for getting specific items
|
||||
}
|
||||
|
||||
private UserManager mUserManager;
|
||||
private Uri.Builder mUriBuilder;
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
if (LOCAL_LOGV) Log.d(TAG, "Creating CMSettingsProvider");
|
||||
|
||||
mUserManager = UserManager.get(getContext());
|
||||
|
||||
establishDbTracking(UserHandle.USER_OWNER);
|
||||
|
||||
mUriBuilder = new Uri.Builder();
|
||||
mUriBuilder.scheme(ContentResolver.SCHEME_CONTENT);
|
||||
mUriBuilder.authority(CMSettings.AUTHORITY);
|
||||
|
||||
// TODO Add migration for cm settings
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
|
||||
String sortOrder) {
|
||||
if (uri == null) {
|
||||
throw new IllegalArgumentException("Uri cannot be null");
|
||||
}
|
||||
|
||||
String tableName = getTableNameFromUri(uri);
|
||||
checkWritePermissions(tableName);
|
||||
|
||||
int callingUserId = UserHandle.getCallingUserId();
|
||||
CMDatabaseHelper dbHelper = getOrEstablishDatabase(getUserIdForTable(tableName,
|
||||
callingUserId));
|
||||
SQLiteDatabase db = dbHelper.getReadableDatabase();
|
||||
|
||||
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
|
||||
queryBuilder.setTables(tableName);
|
||||
|
||||
Cursor returnCursor = queryBuilder.query(db, projection, selection, selectionArgs, null,
|
||||
null, sortOrder);
|
||||
// the default Cursor interface does not support per-user observation
|
||||
try {
|
||||
AbstractCursor abstractCursor = (AbstractCursor) returnCursor;
|
||||
abstractCursor.setNotificationUri(getContext().getContentResolver(), uri,
|
||||
callingUserId);
|
||||
} catch (ClassCastException e) {
|
||||
// details of the concrete Cursor implementation have changed and this code has
|
||||
// not been updated to match -- complain and fail hard.
|
||||
Log.wtf(TAG, "Incompatible cursor derivation");
|
||||
throw e;
|
||||
}
|
||||
|
||||
return returnCursor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType(Uri uri) {
|
||||
// TODO: Implement
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int bulkInsert(Uri uri, ContentValues[] values) {
|
||||
return bulkInsertForUser(UserHandle.getCallingUserId(), uri, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a bulk insert for a specific user.
|
||||
* @param userId The user id to perform the bulk insert for.
|
||||
* @param uri The content:// URI of the insertion request.
|
||||
* @param values An array of sets of column_name/value pairs to add to the database.
|
||||
* This must not be {@code null}.
|
||||
* @return Number of rows inserted.
|
||||
*/
|
||||
int bulkInsertForUser(int userId, Uri uri, ContentValues[] values) {
|
||||
if (uri == null) {
|
||||
throw new IllegalArgumentException("Uri cannot be null");
|
||||
}
|
||||
|
||||
if (values == null) {
|
||||
throw new IllegalArgumentException("ContentValues cannot be null");
|
||||
}
|
||||
|
||||
int numRowsAffected = 0;
|
||||
|
||||
String tableName = getTableNameFromUri(uri);
|
||||
checkWritePermissions(tableName);
|
||||
|
||||
CMDatabaseHelper dbHelper = getOrEstablishDatabase(getUserIdForTable(tableName, userId));
|
||||
SQLiteDatabase db = dbHelper.getWritableDatabase();
|
||||
|
||||
db.beginTransaction();
|
||||
try {
|
||||
for (ContentValues value : values) {
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
long rowId = db.insert(tableName, null, value);
|
||||
|
||||
if (rowId >= 0) {
|
||||
numRowsAffected++;
|
||||
|
||||
if (LOCAL_LOGV) Log.d(TAG, tableName + " <- " + values);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
db.close();
|
||||
}
|
||||
|
||||
if (numRowsAffected > 0) {
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
notifyChange(uri, tableName, userId);
|
||||
if (LOCAL_LOGV) Log.d(TAG, tableName + ": " + numRowsAffected + " row(s) inserted");
|
||||
}
|
||||
|
||||
return numRowsAffected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri insert(Uri uri, ContentValues values) {
|
||||
if (uri == null) {
|
||||
throw new IllegalArgumentException("Uri cannot be null");
|
||||
}
|
||||
|
||||
if (values == null) {
|
||||
throw new IllegalArgumentException("ContentValues cannot be null");
|
||||
}
|
||||
|
||||
String tableName = getTableNameFromUri(uri);
|
||||
checkWritePermissions(tableName);
|
||||
|
||||
int callingUserId = UserHandle.getCallingUserId();
|
||||
CMDatabaseHelper dbHelper = getOrEstablishDatabase(getUserIdForTable(tableName,
|
||||
callingUserId));
|
||||
|
||||
long rowId = -1;
|
||||
|
||||
SQLiteDatabase db = dbHelper.getWritableDatabase();
|
||||
try {
|
||||
rowId = db.insert(tableName, null, values);
|
||||
} finally {
|
||||
db.close();
|
||||
}
|
||||
|
||||
Uri returnUri = null;
|
||||
if (rowId != -1) {
|
||||
returnUri = ContentUris.withAppendedId(uri, rowId);
|
||||
notifyChange(returnUri, tableName, callingUserId);
|
||||
if (LOCAL_LOGV) Log.d(TAG, "Inserted row id: " + rowId + " into tableName: " +
|
||||
tableName);
|
||||
}
|
||||
|
||||
return returnUri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
||||
if (uri == null) {
|
||||
throw new IllegalArgumentException("Uri cannot be null");
|
||||
}
|
||||
|
||||
int numRowsAffected = 0;
|
||||
|
||||
// Allow only selection by key; a null/empty selection string will cause all rows in the
|
||||
// table to be deleted
|
||||
if (!TextUtils.isEmpty(selection) && selectionArgs.length > 0) {
|
||||
String tableName = getTableNameFromUri(uri);
|
||||
checkWritePermissions(tableName);
|
||||
|
||||
int callingUserId = UserHandle.getCallingUserId();
|
||||
CMDatabaseHelper dbHelper = getOrEstablishDatabase(getUserIdForTable(tableName,
|
||||
callingUserId));
|
||||
SQLiteDatabase db = dbHelper.getWritableDatabase();
|
||||
|
||||
try {
|
||||
numRowsAffected = db.delete(tableName, selection, selectionArgs);
|
||||
} finally {
|
||||
db.close();
|
||||
}
|
||||
|
||||
if (numRowsAffected > 0) {
|
||||
notifyChange(uri, tableName, callingUserId);
|
||||
if (LOCAL_LOGV) Log.d(TAG, tableName + ": " + numRowsAffected + " row(s) deleted");
|
||||
}
|
||||
}
|
||||
|
||||
return numRowsAffected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||
if (uri == null) {
|
||||
throw new IllegalArgumentException("Uri cannot be null");
|
||||
}
|
||||
|
||||
if (values == null) {
|
||||
throw new IllegalArgumentException("ContentValues cannot be null");
|
||||
}
|
||||
|
||||
String tableName = getTableNameFromUri(uri);
|
||||
checkWritePermissions(tableName);
|
||||
|
||||
int callingUserId = UserHandle.getCallingUserId();
|
||||
CMDatabaseHelper dbHelper = getOrEstablishDatabase(getUserIdForTable(tableName,
|
||||
callingUserId));
|
||||
|
||||
int numRowsAffected = 0;
|
||||
|
||||
SQLiteDatabase db = dbHelper.getWritableDatabase();
|
||||
try {
|
||||
numRowsAffected = db.update(tableName, values, selection, selectionArgs);
|
||||
} finally {
|
||||
db.close();
|
||||
}
|
||||
|
||||
if (numRowsAffected > 0) {
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
if (LOCAL_LOGV) Log.d(TAG, tableName + ": " + numRowsAffected + " row(s) updated");
|
||||
}
|
||||
|
||||
return numRowsAffected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to get a {@link CMDatabaseHelper} for the specified user and if it does not exist, a
|
||||
* new instance of {@link CMDatabaseHelper} is created for the specified user and returned.
|
||||
* @param callingUser
|
||||
* @return
|
||||
*/
|
||||
private CMDatabaseHelper getOrEstablishDatabase(int callingUser) {
|
||||
if (callingUser >= android.os.Process.SYSTEM_UID) {
|
||||
if (USER_CHECK_THROWS) {
|
||||
throw new IllegalArgumentException("Uid rather than user handle: " + callingUser);
|
||||
} else {
|
||||
Log.wtf(TAG, "Establish db for uid rather than user: " + callingUser);
|
||||
}
|
||||
}
|
||||
|
||||
long oldId = Binder.clearCallingIdentity();
|
||||
try {
|
||||
CMDatabaseHelper dbHelper;
|
||||
synchronized (this) {
|
||||
dbHelper = mDbHelpers.get(callingUser);
|
||||
}
|
||||
if (null == dbHelper) {
|
||||
establishDbTracking(callingUser);
|
||||
synchronized (this) {
|
||||
dbHelper = mDbHelpers.get(callingUser);
|
||||
}
|
||||
}
|
||||
return dbHelper;
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(oldId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a {@link CMDatabaseHelper} exists for a user and if it doesn't, a new helper is
|
||||
* created and added to the list of tracked database helpers
|
||||
* @param userId
|
||||
*/
|
||||
private void establishDbTracking(int userId) {
|
||||
CMDatabaseHelper dbHelper;
|
||||
|
||||
synchronized (this) {
|
||||
dbHelper = mDbHelpers.get(userId);
|
||||
if (LOCAL_LOGV) {
|
||||
Log.i(TAG, "Checking cm settings db helper for user " + userId);
|
||||
}
|
||||
if (dbHelper == null) {
|
||||
if (LOCAL_LOGV) {
|
||||
Log.i(TAG, "Installing new cm settings db helper for user " + userId);
|
||||
}
|
||||
dbHelper = new CMDatabaseHelper(getContext(), userId);
|
||||
mDbHelpers.append(userId, dbHelper);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialization of the db *outside* the locks. It's possible that racing
|
||||
// threads might wind up here, the second having read the cache entries
|
||||
// written by the first, but that's benign: the SQLite helper implementation
|
||||
// manages concurrency itself, and it's important that we not run the db
|
||||
// initialization with any of our own locks held, so we're fine.
|
||||
SQLiteDatabase db = null;
|
||||
try {
|
||||
db = dbHelper.getWritableDatabase();
|
||||
} catch (SQLiteCantOpenDatabaseException ex){
|
||||
Log.e(TAG, "Unable to open writable database for user: " + userId, ex);
|
||||
} finally {
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure the caller has permission to write this data.
|
||||
* @param tableName supplied by the caller
|
||||
* @throws SecurityException if the caller is forbidden to write.
|
||||
*/
|
||||
private void checkWritePermissions(String tableName) {
|
||||
if ((CMDatabaseHelper.CMTableNames.TABLE_SECURE.equals(tableName) ||
|
||||
CMDatabaseHelper.CMTableNames.TABLE_GLOBAL.equals(tableName)) &&
|
||||
getContext().checkCallingOrSelfPermission(
|
||||
cyanogenmod.platform.Manifest.permission.WRITE_SECURE_SETTINGS) !=
|
||||
PackageManager.PERMISSION_GRANTED) {
|
||||
throw new SecurityException(
|
||||
String.format("Permission denial: writing to cm secure settings requires %1$s",
|
||||
cyanogenmod.platform.Manifest.permission.WRITE_SECURE_SETTINGS));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utilizes an {@link UriMatcher} to check for a valid combination of scheme, authority, and
|
||||
* path and returns the corresponding table name
|
||||
* @param uri
|
||||
* @return Table name
|
||||
*/
|
||||
private String getTableNameFromUri(Uri uri) {
|
||||
int code = sUriMatcher.match(uri);
|
||||
|
||||
switch (code) {
|
||||
case SYSTEM:
|
||||
return CMDatabaseHelper.CMTableNames.TABLE_SYSTEM;
|
||||
case SECURE:
|
||||
return CMDatabaseHelper.CMTableNames.TABLE_SECURE;
|
||||
case GLOBAL:
|
||||
return CMDatabaseHelper.CMTableNames.TABLE_GLOBAL;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid uri: " + uri);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the table is Global, the owner's user id is returned. Otherwise, the original user id
|
||||
* is returned.
|
||||
* @param tableName
|
||||
* @param userId
|
||||
* @return User id
|
||||
*/
|
||||
private int getUserIdForTable(String tableName, int userId) {
|
||||
return CMDatabaseHelper.CMTableNames.TABLE_GLOBAL.equals(tableName) ?
|
||||
UserHandle.USER_OWNER : userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify setting version for an updated table before notifying of change. The
|
||||
* {@link CMSettings} class uses these to provide client-side caches.
|
||||
* @param uri to send notifications for
|
||||
* @param userId
|
||||
*/
|
||||
private void notifyChange(Uri uri, String tableName, int userId) {
|
||||
String property = null;
|
||||
final boolean isGlobal = tableName.equals(CMDatabaseHelper.CMTableNames.TABLE_GLOBAL);
|
||||
if (tableName.equals(CMDatabaseHelper.CMTableNames.TABLE_SYSTEM)) {
|
||||
property = CMSettings.System.SYS_PROP_CM_SETTING_VERSION;
|
||||
} else if (tableName.equals(CMDatabaseHelper.CMTableNames.TABLE_SECURE)) {
|
||||
property = CMSettings.Secure.SYS_PROP_CM_SETTING_VERSION;
|
||||
} else if (isGlobal) {
|
||||
property = CMSettings.Global.SYS_PROP_CM_SETTING_VERSION;
|
||||
}
|
||||
|
||||
if (property != null) {
|
||||
long version = SystemProperties.getLong(property, 0) + 1;
|
||||
if (LOCAL_LOGV) Log.v(TAG, "property: " + property + "=" + version);
|
||||
SystemProperties.set(property, Long.toString(version));
|
||||
}
|
||||
|
||||
final int notifyTarget = isGlobal ? UserHandle.USER_ALL : userId;
|
||||
final long oldId = Binder.clearCallingIdentity();
|
||||
try {
|
||||
getContext().getContentResolver().notifyChange(uri, null, true, notifyTarget);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(oldId);
|
||||
}
|
||||
if (LOCAL_LOGV) Log.v(TAG, "notifying for " + notifyTarget + ": " + uri);
|
||||
}
|
||||
|
||||
// TODO Add caching
|
||||
}
|
34
packages/CMSettingsProvider/tests/Android.mk
Normal file
34
packages/CMSettingsProvider/tests/Android.mk
Normal file
@ -0,0 +1,34 @@
|
||||
#
|
||||
# Copyright (C) 2015 The CyanogenMod Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
|
||||
LOCAL_PACKAGE_NAME := CMSettingsProviderTests
|
||||
LOCAL_INSTRUMENTATION_FOR := CMSettingsProvider
|
||||
|
||||
LOCAL_SRC_FILES := $(call all-subdir-java-files)
|
||||
|
||||
LOCAL_CERTIFICATE := platform
|
||||
LOCAL_JAVA_LIBRARIES := android.test.runner
|
||||
LOCAL_PROGUARD_ENABLED := disabled
|
||||
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := \
|
||||
org.cyanogenmod.platform.sdk
|
||||
|
||||
include $(BUILD_PACKAGE)
|
30
packages/CMSettingsProvider/tests/AndroidManifest.xml
Normal file
30
packages/CMSettingsProvider/tests/AndroidManifest.xml
Normal file
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2015 The CyanogenMod Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.cyanogenmod.cmsettings.tests">
|
||||
|
||||
<uses-permission android:name="cyanogenmod.permission.WRITE_SETTINGS"/>
|
||||
<uses-permission android:name="cyanogenmod.permission.WRITE_SECURE_SETTINGS"/>
|
||||
|
||||
<instrumentation
|
||||
android:name="android.test.InstrumentationTestRunner"
|
||||
android:targetPackage="org.cyanogenmod.cmsettings.tests"
|
||||
android:label="CM Settings Provider Tests" />
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
</application>
|
||||
</manifest>
|
@ -0,0 +1,159 @@
|
||||
/**
|
||||
* Copyright (c) 2015, The CyanogenMod 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 org.cyanogenmod.cmsettings.tests;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.suitebuilder.annotation.MediumTest;
|
||||
import android.util.Log;
|
||||
import cyanogenmod.providers.CMSettings;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class CMSettingsProviderTest extends AndroidTestCase {
|
||||
private static final String TAG = "CMSettingsProviderTest";
|
||||
|
||||
private static final LinkedHashMap<String, String> mMap = new LinkedHashMap<String, String>();
|
||||
|
||||
static {
|
||||
mMap.put("testKey1", "value1");
|
||||
mMap.put("testKey2", "value2");
|
||||
mMap.put("testKey3", "value3");
|
||||
}
|
||||
|
||||
private static final String[] PROJECTIONS = new String[] { "name", "value" };
|
||||
|
||||
private ContentResolver mContentResolver;
|
||||
|
||||
@Override
|
||||
public void setUp() {
|
||||
mContentResolver = mContext.getContentResolver();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testBulkInsertSuccess() {
|
||||
Log.d(TAG, "Starting bulk insert test");
|
||||
|
||||
ContentValues[] contentValues = new ContentValues[mMap.size()];
|
||||
int count = 0;
|
||||
for (Map.Entry<String, String> kVPair : mMap.entrySet()) {
|
||||
ContentValues contentValue = new ContentValues();
|
||||
contentValue.put(PROJECTIONS[0], kVPair.getKey());
|
||||
contentValue.put(PROJECTIONS[1], kVPair.getValue());
|
||||
contentValues[count++] = contentValue;
|
||||
}
|
||||
|
||||
testBulkInsertForUri(CMSettings.System.CONTENT_URI, contentValues);
|
||||
testBulkInsertForUri(CMSettings.Secure.CONTENT_URI, contentValues);
|
||||
testBulkInsertForUri(CMSettings.Global.CONTENT_URI, contentValues);
|
||||
|
||||
Log.d(TAG, "Finished bulk insert test");
|
||||
}
|
||||
|
||||
private void testBulkInsertForUri(Uri uri, ContentValues[] contentValues) {
|
||||
int rowsInserted = mContentResolver.bulkInsert(uri, contentValues);
|
||||
assertEquals(mMap.size(), rowsInserted);
|
||||
|
||||
Cursor queryCursor = mContentResolver.query(uri, PROJECTIONS, null, null, null);
|
||||
try {
|
||||
while (queryCursor.moveToNext()) {
|
||||
assertEquals(PROJECTIONS.length, queryCursor.getColumnCount());
|
||||
|
||||
String actualKey = queryCursor.getString(0);
|
||||
assertTrue(mMap.containsKey(actualKey));
|
||||
|
||||
assertEquals(mMap.get(actualKey), queryCursor.getString(1));
|
||||
}
|
||||
|
||||
Log.d(TAG, "Test successful");
|
||||
}
|
||||
finally {
|
||||
queryCursor.close();
|
||||
}
|
||||
|
||||
// TODO: Find a better way to cleanup database/use ProviderTestCase2 without process crash
|
||||
for (String key : mMap.keySet()) {
|
||||
mContentResolver.delete(uri, PROJECTIONS[0] + " = ?", new String[]{ key });
|
||||
}
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testInsertUpdateDeleteSuccess() {
|
||||
Log.d(TAG, "Starting insert/update/delete test");
|
||||
|
||||
testInsertUpdateDeleteForUri(CMSettings.System.CONTENT_URI);
|
||||
testInsertUpdateDeleteForUri(CMSettings.Secure.CONTENT_URI);
|
||||
testInsertUpdateDeleteForUri(CMSettings.Global.CONTENT_URI);
|
||||
|
||||
Log.d(TAG, "Finished insert/update/delete test");
|
||||
}
|
||||
|
||||
private void testInsertUpdateDeleteForUri(Uri uri) {
|
||||
String key1 = "testKey1";
|
||||
String value1 = "value1";
|
||||
String value2 = "value2";
|
||||
|
||||
// test insert
|
||||
ContentValues contentValue = new ContentValues();
|
||||
contentValue.put(PROJECTIONS[0], key1);
|
||||
contentValue.put(PROJECTIONS[1], value1);
|
||||
|
||||
mContentResolver.insert(uri, contentValue);
|
||||
|
||||
// check insert
|
||||
Cursor queryCursor = mContentResolver.query(uri, PROJECTIONS, null, null, null);
|
||||
assertEquals(1, queryCursor.getCount());
|
||||
|
||||
queryCursor.moveToNext();
|
||||
assertEquals(PROJECTIONS.length, queryCursor.getColumnCount());
|
||||
|
||||
String actualKey = queryCursor.getString(0);
|
||||
assertEquals(key1, actualKey);
|
||||
assertEquals(value1, queryCursor.getString(1));
|
||||
|
||||
// test update
|
||||
contentValue.clear();
|
||||
contentValue.put(PROJECTIONS[1], value2);
|
||||
|
||||
int rowsAffected = mContentResolver.update(uri, contentValue, PROJECTIONS[0] + " = ?",
|
||||
new String[]{key1});
|
||||
assertEquals(1, rowsAffected);
|
||||
|
||||
// check update
|
||||
queryCursor = mContentResolver.query(uri, PROJECTIONS, null, null, null);
|
||||
assertEquals(1, queryCursor.getCount());
|
||||
|
||||
queryCursor.moveToNext();
|
||||
assertEquals(PROJECTIONS.length, queryCursor.getColumnCount());
|
||||
|
||||
actualKey = queryCursor.getString(0);
|
||||
assertEquals(key1, actualKey);
|
||||
assertEquals(value2, queryCursor.getString(1));
|
||||
|
||||
// test delete
|
||||
rowsAffected = mContentResolver.delete(uri, PROJECTIONS[0] + " = ?", new String[]{key1});
|
||||
assertEquals(1, rowsAffected);
|
||||
|
||||
// check delete
|
||||
queryCursor = mContentResolver.query(uri, PROJECTIONS, null, null, null);
|
||||
assertEquals(0, queryCursor.getCount());
|
||||
}
|
||||
}
|
1044
src/java/cyanogenmod/providers/CMSettings.java
Normal file
1044
src/java/cyanogenmod/providers/CMSettings.java
Normal file
File diff suppressed because it is too large
Load Diff
@ -442,6 +442,8 @@ package cyanogenmod.platform {
|
||||
field public static final java.lang.String MODIFY_SOUND_SETTINGS = "cyanogenmod.permission.MODIFY_SOUND_SETTINGS";
|
||||
field public static final java.lang.String PUBLISH_CUSTOM_TILE = "cyanogenmod.permission.PUBLISH_CUSTOM_TILE";
|
||||
field public static final java.lang.String READ_MSIM_PHONE_STATE = "cyanogenmod.permission.READ_MSIM_PHONE_STATE";
|
||||
field public static final java.lang.String WRITE_SECURE_SETTINGS = "cyanogenmod.permission.WRITE_SECURE_SETTINGS";
|
||||
field public static final java.lang.String WRITE_SETTINGS = "cyanogenmod.permission.WRITE_SETTINGS";
|
||||
}
|
||||
|
||||
public final class R {
|
||||
@ -566,3 +568,68 @@ package cyanogenmod.profiles {
|
||||
|
||||
}
|
||||
|
||||
package cyanogenmod.providers {
|
||||
|
||||
public final class CMSettings {
|
||||
ctor public CMSettings();
|
||||
field public static final java.lang.String AUTHORITY = "cmsettings";
|
||||
}
|
||||
|
||||
public static class CMSettings.CMSettingNotFoundException extends android.util.AndroidException {
|
||||
ctor public CMSettings.CMSettingNotFoundException(java.lang.String);
|
||||
}
|
||||
|
||||
public static final class CMSettings.Global extends android.provider.Settings.NameValueTable {
|
||||
ctor public CMSettings.Global();
|
||||
method public static float getFloat(android.content.ContentResolver, java.lang.String, float);
|
||||
method public static float getFloat(android.content.ContentResolver, java.lang.String) throws cyanogenmod.providers.CMSettings.CMSettingNotFoundException;
|
||||
method public static int getInt(android.content.ContentResolver, java.lang.String, int);
|
||||
method public static int getInt(android.content.ContentResolver, java.lang.String) throws cyanogenmod.providers.CMSettings.CMSettingNotFoundException;
|
||||
method public static long getLong(android.content.ContentResolver, java.lang.String, long);
|
||||
method public static long getLong(android.content.ContentResolver, java.lang.String) throws cyanogenmod.providers.CMSettings.CMSettingNotFoundException;
|
||||
method public static java.lang.String getString(android.content.ContentResolver, java.lang.String);
|
||||
method public static boolean putFloat(android.content.ContentResolver, java.lang.String, float);
|
||||
method public static boolean putInt(android.content.ContentResolver, java.lang.String, int);
|
||||
method public static boolean putLong(android.content.ContentResolver, java.lang.String, long);
|
||||
method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String);
|
||||
field public static final android.net.Uri CONTENT_URI;
|
||||
field public static final java.lang.String SYS_PROP_CM_SETTING_VERSION = "sys.cm_settings_global_version";
|
||||
}
|
||||
|
||||
public static final class CMSettings.Secure extends android.provider.Settings.NameValueTable {
|
||||
ctor public CMSettings.Secure();
|
||||
method public static float getFloat(android.content.ContentResolver, java.lang.String, float);
|
||||
method public static float getFloat(android.content.ContentResolver, java.lang.String) throws cyanogenmod.providers.CMSettings.CMSettingNotFoundException;
|
||||
method public static int getInt(android.content.ContentResolver, java.lang.String, int);
|
||||
method public static int getInt(android.content.ContentResolver, java.lang.String) throws cyanogenmod.providers.CMSettings.CMSettingNotFoundException;
|
||||
method public static long getLong(android.content.ContentResolver, java.lang.String, long);
|
||||
method public static long getLong(android.content.ContentResolver, java.lang.String) throws cyanogenmod.providers.CMSettings.CMSettingNotFoundException;
|
||||
method public static java.lang.String getString(android.content.ContentResolver, java.lang.String);
|
||||
method public static boolean putFloat(android.content.ContentResolver, java.lang.String, float);
|
||||
method public static boolean putInt(android.content.ContentResolver, java.lang.String, int);
|
||||
method public static boolean putLong(android.content.ContentResolver, java.lang.String, long);
|
||||
method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String);
|
||||
field public static final android.net.Uri CONTENT_URI;
|
||||
field public static final java.lang.String NAME_THEME_CONFIG = "name_theme_config";
|
||||
field public static final java.lang.String SYS_PROP_CM_SETTING_VERSION = "sys.cm_settings_secure_version";
|
||||
}
|
||||
|
||||
public static final class CMSettings.System extends android.provider.Settings.NameValueTable {
|
||||
ctor public CMSettings.System();
|
||||
method public static float getFloat(android.content.ContentResolver, java.lang.String, float);
|
||||
method public static float getFloat(android.content.ContentResolver, java.lang.String) throws cyanogenmod.providers.CMSettings.CMSettingNotFoundException;
|
||||
method public static int getInt(android.content.ContentResolver, java.lang.String, int);
|
||||
method public static int getInt(android.content.ContentResolver, java.lang.String) throws cyanogenmod.providers.CMSettings.CMSettingNotFoundException;
|
||||
method public static long getLong(android.content.ContentResolver, java.lang.String, long);
|
||||
method public static long getLong(android.content.ContentResolver, java.lang.String) throws cyanogenmod.providers.CMSettings.CMSettingNotFoundException;
|
||||
method public static java.lang.String getString(android.content.ContentResolver, java.lang.String);
|
||||
method public static boolean putFloat(android.content.ContentResolver, java.lang.String, float);
|
||||
method public static boolean putInt(android.content.ContentResolver, java.lang.String, int);
|
||||
method public static boolean putLong(android.content.ContentResolver, java.lang.String, long);
|
||||
method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String);
|
||||
field public static final android.net.Uri CONTENT_URI;
|
||||
field public static final java.lang.String SYS_PROP_CM_SETTING_VERSION = "sys.cm_settings_system_version";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
<uses-permission android:name="cyanogenmod.permission.PUBLISH_CUSTOM_TILE" />
|
||||
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
|
||||
<uses-permission android:name="cyanogenmod.permission.WRITE_SETTINGS"/>
|
||||
<uses-permission android:name="cyanogenmod.permission.WRITE_SECURE_SETTINGS"/>
|
||||
<uses-permission android:name="cyanogenmod.permission.MODIFY_NETWORK_SETTINGS" />
|
||||
<uses-permission android:name="cyanogenmod.permission.MODIFY_SOUND_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.REBOOT" />
|
||||
|
118
tests/src/org/cyanogenmod/tests/providers/CMSettingsTest.java
Normal file
118
tests/src/org/cyanogenmod/tests/providers/CMSettingsTest.java
Normal file
@ -0,0 +1,118 @@
|
||||
/**
|
||||
* Copyright (c) 2015, The CyanogenMod 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 org.cyanogenmod.tests.providers;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.provider.Settings;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.suitebuilder.annotation.MediumTest;
|
||||
import cyanogenmod.providers.CMSettings;
|
||||
|
||||
public class CMSettingsTest extends AndroidTestCase{
|
||||
private ContentResolver mContentResolver;
|
||||
|
||||
@Override
|
||||
public void setUp() {
|
||||
mContentResolver = getContext().getContentResolver();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testPutAndGetSystemString() {
|
||||
final String key = "key";
|
||||
|
||||
// put
|
||||
final String expectedValue = "systemTestValue1";
|
||||
boolean isPutSuccessful = CMSettings.System.putString(mContentResolver, key, expectedValue);
|
||||
assertTrue(isPutSuccessful);
|
||||
|
||||
// get
|
||||
String actualValue = CMSettings.System.getString(mContentResolver, key);
|
||||
assertEquals(expectedValue, actualValue);
|
||||
|
||||
// replace
|
||||
final String expectedReplaceValue = "systemTestValue2";
|
||||
isPutSuccessful = CMSettings.System.putString(mContentResolver, key, expectedReplaceValue);
|
||||
assertTrue(isPutSuccessful);
|
||||
|
||||
// get
|
||||
actualValue = CMSettings.System.getString(mContentResolver, key);
|
||||
assertEquals(expectedReplaceValue, actualValue);
|
||||
|
||||
// delete to clean up
|
||||
int rowsAffected = mContentResolver.delete(CMSettings.System.CONTENT_URI, Settings.NameValueTable.NAME + " = ?",
|
||||
new String[]{ key });
|
||||
assertEquals(1, rowsAffected);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testPutAndGetSecureString() {
|
||||
final String key = "key";
|
||||
|
||||
// put
|
||||
final String expectedValue = "secureTestValue1";
|
||||
boolean isPutSuccessful = CMSettings.Secure.putString(mContentResolver, key, expectedValue);
|
||||
assertTrue(isPutSuccessful);
|
||||
|
||||
// get
|
||||
String actualValue = CMSettings.Secure.getString(mContentResolver, key);
|
||||
assertEquals(expectedValue, actualValue);
|
||||
|
||||
// replace
|
||||
final String expectedReplaceValue = "secureTestValue2";
|
||||
isPutSuccessful = CMSettings.Secure.putString(mContentResolver, key, expectedReplaceValue);
|
||||
assertTrue(isPutSuccessful);
|
||||
|
||||
// get
|
||||
actualValue = CMSettings.Secure.getString(mContentResolver, key);
|
||||
assertEquals(expectedReplaceValue, actualValue);
|
||||
|
||||
// delete to clean up
|
||||
int rowsAffected = mContentResolver.delete(CMSettings.Secure.CONTENT_URI, Settings.NameValueTable.NAME + " = ?",
|
||||
new String[]{ key });
|
||||
assertEquals(1, rowsAffected);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testPutAndGetGlobalString() {
|
||||
final String key = "key";
|
||||
|
||||
// put
|
||||
final String expectedValue = "globalTestValue1";
|
||||
boolean isPutSuccessful = CMSettings.Global.putString(mContentResolver, key, expectedValue);
|
||||
assertTrue(isPutSuccessful);
|
||||
|
||||
// get
|
||||
String actualValue = CMSettings.Global.getString(mContentResolver, key);
|
||||
assertEquals(expectedValue, actualValue);
|
||||
|
||||
// replace
|
||||
final String expectedReplaceValue = "globalTestValue2";
|
||||
isPutSuccessful = CMSettings.Global.putString(mContentResolver, key, expectedReplaceValue);
|
||||
assertTrue(isPutSuccessful);
|
||||
|
||||
// get
|
||||
actualValue = CMSettings.Global.getString(mContentResolver, key);
|
||||
assertEquals(expectedReplaceValue, actualValue);
|
||||
|
||||
// delete to clean up
|
||||
int rowsAffected = mContentResolver.delete(CMSettings.Global.CONTENT_URI, Settings.NameValueTable.NAME + " = ?",
|
||||
new String[]{ key });
|
||||
assertEquals(1, rowsAffected);
|
||||
}
|
||||
|
||||
// TODO Add tests for other users
|
||||
}
|
Loading…
Reference in New Issue
Block a user