testing: Host side settings migration verifier.

See README for usage.

Change-Id: Ibcbd4ee3882097a8d01ca219aeef0130778436f6
This commit is contained in:
Adnan Begovic 2015-11-13 15:28:21 -08:00
parent f7f6d14148
commit d5fdee9833
16 changed files with 2357 additions and 0 deletions

View File

@ -0,0 +1,9 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_MODULE:= migration-interface
include $(BUILD_HOST_JAVA_LIBRARY)

13
host/migration/README.md Normal file
View File

@ -0,0 +1,13 @@
## CMSettings Migration Test
The tests host library contains a simple interface which calls down to a client jar on the device
to take a snapshot of the current settings, forces an update with the current build in the tree,
then verifies the settings post migration.
To run the test (on a live device):
``` java -cp /Volumes/CM/CM13/out/host/darwin-x86/framework/migration-interface.jar MigrationTest \
--settings <example settings> --bootimg <boot.img> --systemimg <system.img> ```
To generate example settings to be written against 12.1 device during migration:
```java -cp /Volumes/CM/CM13/out/host/<platform>/framework/migration-interface.jar GenerateExampleSettings <output file path>"

View File

@ -0,0 +1,121 @@
# Settings which are to be moved to CMSettings
# Automatically generated by vendor/cmsdk/host/migration/src/GenerateExampleSettingsGenerateExampleSettings.java.
Row: 0 name=status_bar_battery_style, type=s, value=2, type=s
Row: 1 name=enable_forward_lookup, type=s, value=1, type=s
Row: 2 name=enable_people_lookup, type=s, value=1, type=s
Row: 3 name=enable_reverse_lookup, type=s, value=1, type=s
Row: 4 name=qs_quick_pulldown, type=s, value=1, type=s
Row: 5 name=display_auto_outdoor_mode, type=s, value=-1, type=s
Row: 6 name=home_wake_screen, type=s, value=1, type=s
Row: 7 name=back_wake_screen, type=s, value=0, type=s
Row: 8 name=menu_wake_screen, type=s, value=0, type=s
Row: 9 name=assist_wake_screen, type=s, value=0, type=s
Row: 10 name=app_switch_wake_screen, type=s, value=0, type=s
Row: 11 name=status_bar_clock, type=s, value=2, type=s
Row: 12 name=status_bar_show_battery_percent, type=s, value=1, type=s
Row: 13 name=reverse_lookup_provider, type=s, value=OpenCnam, type=s
Row: 14 name=live_display_hinted, type=s, value=1, type=s
Row: 15 name=status_bar_ime_switcher, type=s, value=1, type=s
Row: 16 name=edge_service_for_gestures, type=s, value=1, type=s
Row: 17 name=people_lookup_provider, type=s, value=WhitePages, type=s
Row: 18 name=forward_lookup_provider, type=s, value=Google, type=s
Row: 19 name=status_bar_show_weather, type=s, value=1, type=s
Row: 20 name=system_profiles_enabled, type=s, value=1, type=s
Row: 21 name=proximity_on_wake, type=s, value=0, type=s
Row: 22 name=display_low_power, type=s, value=1, type=s
Row: 23 name=display_color_enhance, type=s, value=1, type=s
Row: 24 name=increasing_ring_ramp_up_time, type=s, value=10, type=s
Row: 25 name=increasing_ring_start_vol, type=s, value=0.487, type=s
Row: 26 name=display_temperature_night, type=s, value=2800, type=s
Row: 27 name=camera_launch, type=s, value=1, type=s
Row: 28 name=nav_buttons, type=s, value=empty|back|home|recent|empty|menu0, type=s
Row: 29 name=increasing_ring, type=s, value=1, type=s
Row: 30 name=volume_adjust_sounds_enabled, type=s, value=1, type=s
Row: 31 name=display_temperature_mode, type=s, value=2, type=s
Row: 32 name=display_temperature_day, type=s, value=5600, type=s
Row: 33 name=display_color_adjustment, type=s, value=0.999982 0.999982 0.999982, type=s
Row: 34 name=double_tap_sleep_gesture, type=s, value=1, type=s
Row: 35 name=recents_show_search_bar, type=s, value=0, type=s
Row: 36 name=battery_light_enabled, type=s, value=1, type=s
Row: 37 name=battery_light_pulse, type=s, value=1, type=s
Row: 38 name=battery_light_low_color, type=s, value=-55513, type=s
Row: 39 name=battery_light_medium_color, type=s, value=-113, type=s
Row: 40 name=battery_light_full_color, type=s, value=-16722688, type=s
Row: 41 name=notification_light_pulse_default_color, type=s, value=-848184, type=s
Row: 42 name=notification_light_pulse_default_led_on, type=s, value=1000, type=s
Row: 43 name=notification_light_pulse_default_led_off, type=s, value=6000, type=s
Row: 44 name=notification_light_screen_on_enable, type=s, value=0, type=s
Row: 45 name=notification_light_pulse_call_color, type=s, value=-4288625, type=s
Row: 46 name=notification_light_pulse_call_led_on, type=s, value=1000, type=s
Row: 47 name=notification_light_pulse_call_led_off, type=s, value=6000, type=s
Row: 48 name=notification_light_pulse_vmail_color, type=s, value=-4568759, type=s
Row: 49 name=notification_light_pulse_vmail_led_on, type=s, value=1000, type=s
Row: 50 name=notification_light_pulse_vmail_led_off, type=s, value=6000, type=s
Row: 51 name=notification_light_pulse_custom_enable, type=s, value=0, type=s
Row: 52 name=lockscreen_scramble_pin_layout, type=s, value=1, type=s
Row: 53 name=navigation_bar_left, type=s, value=0, type=s
Row: 54 name=navigation_bar_menu_arrow_keys, type=s, value=0, type=s
Row: 55 name=camera_sleep_on_release, type=s, value=0, type=s
Row: 56 name=camera_wake_screen, type=s, value=0, type=s
Row: 57 name=volume_wake_screen, type=s, value=0, type=s
Row: 58 name=volbtn_music_controls, type=s, value=1, type=s
Row: 59 name=volume_keys_control_ring_stream, type=s, value=1, type=s
Row: 60 name=swap_volume_keys_on_rotation, type=s, value=0, type=s
Row: 61 name=status_bar_brightness_control, type=s, value=0, type=s
Row: 62 name=status_bar_notif_count, type=s, value=1, type=s
Row: 63 name=dev_force_show_navbar, type=s, value=0, type=s
Row: 64 name=stats_collection, type=s, value=1, type=s
Row: 65 name=advanced_mode, type=s, value=1, type=s
Row: 66 name=default_theme_package, type=s, value=com.cyngn.hexo, type=s
Row: 67 name=default_theme_components, type=s, value=mods_overlays|mods_status_bar|mods_navigation_bar|mods_icons|mods_homescreen|mods_fonts, type=s
Row: 68 name=live_display_color_matrix, type=s, value=NULL, type=NULL
Row: 69 name=navigation_ring_targets_0, type=s, value=assist, type=s
Row: 70 name=navigation_ring_targets_1, type=s, value=assist, type=s
Row: 71 name=navigation_ring_targets_2, type=s, value=assist, type=s
Row: 72 name=theme_prev_boot_api_level, type=s, value=22, type=s
Row: 73 name=qs_show_brightness_slider, type=s, value=1, type=s
Row: 74 name=recents_long_press_activity, type=s, value=torch, type=s
Row: 75 name=lockscreen_target_actions, type=s, value=default|#Intent;actionandroid.intent.action.MAIN;categoryandroid.intent.category.LAUNCHER;componentcom.snapchat.android/.LandingPageActivity;end, type=s
Row: 76 name=device_hostname, type=s, value=android-932989077997886b, type=s
Row: 77 name=sysui_qs_main_tiles, type=s, value=1, type=s
Row: 78 name=privacy_guard_default, type=s, value=1, type=s
Row: 79 name=protected_components, type=s, value=null, type=s
Row: 80 name=adb_notify, type=s, value=1, type=s
Row: 81 name=development_shortcut, type=s, value=0, type=s
Row: 82 name=app_perf_profiles_enabled, type=s, value=1, type=s
Row: 83 name=performance_profile, type=s, value=1, type=s
Row: 84 name=sysui_qs_tiles, type=s, value=wifi, type=s
Row: 85 name=volume_link_notification, type=s, value=0, type=s
Row: 86 name=power_notifications_ringtone, type=s, value=content://settings/system/notification_sound, type=s
Row: 87 name=power_notifications_vibrate, type=s, value=0, type=s
Row: 88 name=power_notifications_enabled, type=s, value=0, type=s
Row: 89 name=wake_when_plugged_or_unplugged, type=s, value=1, type=s
Row: 90 name=zen_disable_ducking_during_media_playback, type=s, value=0, type=s
Row: 91 name=bluetooth_accept_all_files, type=s, value=0, type=s
Row: 92 name=call_recording_format, type=s, value=0, type=s
Row: 93 name=dialer_opencnam_account_sid, type=s, value=0, type=s
Row: 94 name=dialer_opencnam_auth_token, type=s, value=0, type=s
Row: 95 name=enable_mwi_notification, type=s, value=0, type=s
Row: 96 name=key_app_switch_action, type=s, value=0, type=s
Row: 97 name=key_app_switch_long_press_action, type=s, value=0, type=s
Row: 98 name=key_assist_action, type=s, value=0, type=s
Row: 99 name=key_assist_long_press_action, type=s, value=0, type=s
Row: 100 name=key_home_double_tap_action, type=s, value=0, type=s
Row: 101 name=key_home_long_press_action, type=s, value=0, type=s
Row: 102 name=key_menu_action, type=s, value=0, type=s
Row: 103 name=key_menu_long_press_action, type=s, value=0, type=s
Row: 104 name=notification_light_brightness_level, type=s, value=0, type=s
Row: 105 name=notification_light_multiple_leds_enable, type=s, value=0, type=s
Row: 106 name=notification_light_pulse_custom_values, type=s, value=0, type=s
Row: 107 name=advanced_reboot, type=s, value=0, type=s
Row: 108 name=button_backlight_timeout, type=s, value=0, type=s
Row: 109 name=button_brightness, type=s, value=255, type=s
Row: 110 name=keyboard_brightness, type=s, value=255, type=s
Row: 111 name=kill_app_longpress_back, type=s, value=0, type=s
Row: 112 name=power_menu_actions, type=s, value=blah, type=s
Row: 113 name=wifi_auto_priority, type=s, value=0, type=s
Row: 114 name=ring_home_button_behavior, type=s, value=1, type=s
Row: 115 name=show_alarm_icon, type=s, value=1, type=s
Row: 116 name=status_bar_am_pm, type=s, value=1, type=s
Row: 117 name=status_bar_quick_qs_pulldown, type=s, value=1, type=s
Row: 118 name=t9_search_input_locale, type=s, value=enUS, type=s

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,80 @@
/*
* 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.
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Created by adnan on 11/13/15.
*/
public class Command implements Runnable {
private String authority;
/**
* Override for execution
*/
@Override
public void run() {
}
/**
* Copies from one stream to another.
*/
protected static void copy(InputStream in, OutputStream out) {
byte[] buffer = new byte[1024];
int read;
try {
while ((read = in.read(buffer)) > -1) {
out.write(buffer, 0, read);
}
} catch (IOException e) {
e.printStackTrace();
}
}
protected static boolean filter(String uri, Setting setting) {
switch (uri) {
case SettingsConstants.SYSTEM:
if (!CMSettings.System.isLegacySetting(setting.getKey())) {
return true;
}
break;
case SettingsConstants.SECURE:
if (SettingsConstants.Ignorables.SECURE_SETTINGS.contains(setting.getKey())) {
return true;
}
if (!CMSettings.Secure.isLegacySetting(setting.getKey())) {
return true;
}
break;
case SettingsConstants.GLOBAL:
if (!CMSettings.Global.isLegacySetting(setting.getKey())) {
return true;
}
break;
}
return false;
}
public void prepend(String authority) {
this.authority = authority;
}
public String getAuthority() {
return authority;
}
}

View File

@ -0,0 +1,22 @@
/*
* 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.
*/
/**
* Created by adnan on 11/16/15.
*/
public interface CommandExecutor {
public void execute();
}

View File

@ -0,0 +1,157 @@
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Created by adnan on 11/17/15.
*/
public class FastbootCommand extends Command {
private static final int MAX_RETRIES = 20;
private static final String[] ADB_REBOOT_BOOTLOADER = new String[] {
"adb", "reboot", "bootloader"
};
private static final String[] ADB_CHECK_BOOT_COMPLETE = new String[] {
"adb", "shell", "getprop", "sys.boot_completed"
};
private static final String FASTBOOT_COMMAND = "fastboot";
private static final String REBOOT = "reboot";
private static final String DEVICES = "devices";
private static final String FLASH = "flash";
private String[] baseCommand;
private String baseArg;
private String image;
private String targetImage;
public FastbootCommand(int command, String[] args) {
switch (command) {
case Types.ADB_REBOOT_BOOTLOADER:
baseCommand = ADB_REBOOT_BOOTLOADER;
break;
case Types.FASTBOOT_FLASH:
baseCommand = new String[] { FASTBOOT_COMMAND };
baseArg = FLASH;
image = args[0];
targetImage = args[1];
break;
case Types.FASTBOOT_DEVICES:
baseCommand = new String[] { FASTBOOT_COMMAND };
baseArg = DEVICES;
break;
case Types.FASTBOOT_REBOOT:
baseCommand = new String[] { FASTBOOT_COMMAND };
baseArg = REBOOT;
break;
default:
throw new UnsupportedOperationException("Unsupported operation " + command);
}
}
@Override
public void run() {
List<String> commandList = new ArrayList<String>(
baseCommand.length + 1);
commandList.addAll(Arrays.asList(baseCommand));
if (baseArg != null && baseArg.length() > 0) {
commandList.add(baseArg);
}
if (image != null &&image.length() > 0) {
commandList.add(image);
}
if (targetImage != null &&targetImage.length() > 0) {
commandList.add(targetImage);
}
String[] commands = commandList.toArray(new String[commandList.size()]);
if (MigrationTest.DEBUG) {
System.out.println("Using commands: " + Arrays.toString(commands));
}
try {
final Process process = Runtime.getRuntime().exec(commands);
final InputStream err = process.getErrorStream();
// Send error output to stderr.
Thread errThread = new Thread() {
@Override
public void run() {
copy(err, System.err);
}
};
errThread.setDaemon(true);
errThread.start();
BufferedReader in = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String rx = in.readLine();
if (rx != null) {
if (MigrationTest.DEBUG) {
System.out.println("Received response " + rx);
}
}
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
in.close();
err.close();
//Gross
if (baseArg != null && baseArg.equals(REBOOT)) {
List<String> secondCommandList = new ArrayList<String>(
ADB_CHECK_BOOT_COMPLETE.length + 1);
secondCommandList.addAll(Arrays.asList(ADB_CHECK_BOOT_COMPLETE));
String[] secondCommands = secondCommandList.toArray(
new String[secondCommandList.size()]);
if (MigrationTest.DEBUG) {
System.out.println("Using commands: " + Arrays.toString(secondCommands));
}
Process process2;
BufferedReader in2;
String line2;
for (int i = 1; i < MAX_RETRIES; i++) {
process2 = Runtime.getRuntime().exec(secondCommands);
in2 = new BufferedReader(
new InputStreamReader(process2.getInputStream()));
if ((line2 = in2.readLine()) != null) {
if (line2.equals("1")) {
in2.close();
process2.destroy();
try {
System.out.println("Device up detected...");
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
}
}
try {
System.out.println("Waiting for device to come up...");
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
in2.close();
process2.destroy();
}
}
process.destroy();
} catch (IOException e) {
System.err.println("Error ");
e.printStackTrace();
}
}
public final class Types {
public static final int ADB_REBOOT_BOOTLOADER = -1;
public static final int FASTBOOT_FLASH = 0;
public static final int FASTBOOT_DEVICES = 1;
public static final int FASTBOOT_REBOOT = 2;
}
}

View File

@ -0,0 +1,90 @@
/*
* 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.
*/
import java.io.File;
import java.io.IOException;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.Charset;
import java.util.ArrayList;
/**
* This is to be run on a live CM 12.1 device.
*/
public class GenerateExampleSettings {
private static ArrayList<Setting> androidSystemSettingList = new ArrayList<Setting>();
private static ArrayList<Setting> androidSecureSettingList = new ArrayList<Setting>();
private static ArrayList<Setting> androidGlobalSettingList = new ArrayList<Setting>();
private static ArrayList<Setting> defaultSettings = new ArrayList<Setting>();
public static void main(String[] args) throws IOException, ClassNotFoundException {
if (args.length != 1) {
System.err.println("Usage: GenerateExampleSettings [target file]");
System.exit(-1);
}
String rootFile = args[0];
SettingImageCommands androidSettingImage =
new SettingImageCommands(SettingsConstants.SETTINGS_AUTHORITY);
androidSettingImage.addQuery(SettingsConstants.SYSTEM, androidSystemSettingList);
androidSettingImage.addQuery(SettingsConstants.SECURE, androidSecureSettingList);
androidSettingImage.addQuery(SettingsConstants.GLOBAL, androidGlobalSettingList);
androidSettingImage.execute();
for (Setting system : androidSystemSettingList) {
if (CMSettings.System.isLegacySetting(system.getKey())) {
defaultSettings.add(system);
}
}
for (Setting secure : androidSecureSettingList) {
if (CMSettings.Secure.isLegacySetting(secure.getKey())) {
defaultSettings.add(secure);
}
}
for (Setting global : androidGlobalSettingList) {
if (CMSettings.Global.isLegacySetting(global.getKey())) {
defaultSettings.add(global);
}
}
Writer out = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(rootFile),
Charset.forName("US-ASCII")));
out.write("# Settings which are to be moved to CMSettings\n");
out.write("# Automatically generated by " +
"vendor/cmsdk/host/migration/src/GenerateExampleSettings"
+ GenerateExampleSettings.class.getSimpleName() + ".java.\n");
// Write settings to file for output.
for (int i = 0; i < defaultSettings.size(); i++) {
Setting defaultSetting = defaultSettings.get(i);
// This is the same format as what is spit out by system/bin/content
out.write("Row: " + i + " name=" + defaultSetting.getKey()
+ ", type=" + defaultSetting.getKeyType()
+ ", value=" + defaultSetting.getValue()
+ ", type=" + defaultSetting.getValueType() + "\n");
}
out.close();
System.out.println("Settings written: " + rootFile.toString());
}
}

View File

@ -0,0 +1,96 @@
/*
* 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.
*/
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
/**
* Essentially:
* adb shell content insert --uri content://settings/secure
* --bind name:s:new_setting --bind value:s:new_value
*/
public class InsertCommand extends Command {
private static final String[] INSERT_SETTINGS = {
"adb", "shell", "content", "insert", "--uri" };
private String targetUri;
private Setting targetSetting;
public InsertCommand(String targetUri, Setting targetSetting) {
this.targetUri = targetUri;
this.targetSetting = targetSetting;
}
@Override
public void run() {
System.out.println("\nWriting setting " + targetSetting.getKey() + " for authority "
+ getAuthority() + " for target uri " + targetUri + "...");
insert(targetUri, targetSetting);
synchronized (this) {
notifyAll();
}
}
private void insert(String uri, Setting setting) {
String[] commands = INSERT_SETTINGS;
List<String> commandList = new ArrayList<String>(
INSERT_SETTINGS.length + 1);
commandList.addAll(Arrays.asList(commands));
commandList.add(SettingsConstants.CONTENT_URI + getAuthority() + uri);
commandList.add("--bind name:" + setting.getKeyType() + ":" + setting.getKey());
commandList.add("--bind value:" + setting.getValueType() + ":"
+ "\"" + setting.getValue() + "\"");
commands = commandList.toArray(new String[commandList.size()]);
if (MigrationTest.DEBUG) {
System.out.println("Using commands: " + Arrays.toString(commands));
}
try {
final Process process = Runtime.getRuntime().exec(commands);
final InputStream err = process.getErrorStream();
// Send error output to stderr.
Thread errThread = new Thread() {
@Override
public void run() {
copy(err, System.err);
}
};
errThread.setDaemon(true);
errThread.start();
BufferedReader in = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String rx = in.readLine();
if (rx != null) {
if (MigrationTest.DEBUG) {
System.out.println("Received response " + rx);
}
}
in.close();
err.close();
process.destroy();
} catch (IOException e) {
System.err.println("Error ");
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,199 @@
/*
* 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.
*/
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
/**
* A verbose settings migration test
*/
class MigrationTest {
private static final String ARGUMENT_SETTINGS = "--settings";
private static final String ARGUMENT_BOOT_IMG = "--bootimg";
private static final String ARGUMENT_SYSTEM_IMG = "--systemimg";
private static final String ARGUMENT_PREFIX = "--";
public static final boolean DEBUG = true;
private static ArrayList<Setting> cmSystemSettingList = new ArrayList<Setting>();
private static ArrayList<Setting> cmSecureSettingList = new ArrayList<Setting>();
private static ArrayList<Setting> cmGlobalSettingList = new ArrayList<Setting>();
private static ArrayList<Setting> legacySystemSettings = new ArrayList<Setting>();
private static ArrayList<Setting> legacySecureSettings = new ArrayList<Setting>();
private static ArrayList<Setting> legacyGlobalSettings = new ArrayList<Setting>();
private static Tokenizer tokenizer;
public static void main(String[] args) throws IOException {
if (args.length < 1) {
showUsage();
System.exit(-1);
}
tokenizer = new Tokenizer(args);
String settingFileName = null;
String bootImage = null;
String systemImage = null;
for (String argument; (argument = tokenizer.nextArg())!= null;) {
if (ARGUMENT_SETTINGS.equals(argument)) {
settingFileName = argumentValueRequired(argument);
} else if (ARGUMENT_BOOT_IMG.equals(argument)) {
bootImage = argumentValueRequired(argument);
} else if (ARGUMENT_SYSTEM_IMG.equals(argument)) {
systemImage = argumentValueRequired(argument);
}
}
if (!new File(settingFileName).exists()) {
System.err.print("Invalid file provided " + settingFileName);
System.exit(-1);
}
SettingImageCommands legacySettings =
new SettingImageCommands(SettingsConstants.SETTINGS_AUTHORITY);
legacySettings.addRead(settingFileName, SettingsConstants.SYSTEM, legacySystemSettings);
legacySettings.addRead(settingFileName, SettingsConstants.SECURE, legacySecureSettings);
legacySettings.addRead(settingFileName, SettingsConstants.GLOBAL, legacyGlobalSettings);
//Read settings
legacySettings.execute();
SettingImageCommands legacyToCMSettings =
new SettingImageCommands(SettingsConstants.SETTINGS_AUTHORITY);
//For each example setting in the table, add inserts
for (Setting setting : legacySystemSettings) {
legacyToCMSettings.addInsert(SettingsConstants.SYSTEM, setting);
}
for (Setting setting : legacySecureSettings) {
legacyToCMSettings.addInsert(SettingsConstants.SECURE, setting);
}
for (Setting setting : legacyGlobalSettings) {
legacyToCMSettings.addInsert(SettingsConstants.GLOBAL, setting);
}
//Write them to the database for verification later
legacyToCMSettings.execute();
//Force update
SettingImageCommands updateRom = new SettingImageCommands(null);
updateRom.addFastboot(FastbootCommand.Types.ADB_REBOOT_BOOTLOADER, null);
updateRom.addFastboot(FastbootCommand.Types.FASTBOOT_DEVICES, null);
updateRom.addFastboot(FastbootCommand.Types.FASTBOOT_FLASH,
new String[]{"boot", bootImage});
updateRom.addFastboot(FastbootCommand.Types.FASTBOOT_FLASH,
new String[]{"system", systemImage});
updateRom.addFastboot(FastbootCommand.Types.FASTBOOT_REBOOT, null);
updateRom.execute();
//Requery
SettingImageCommands cmSettingImage =
new SettingImageCommands(SettingsConstants.CMSETTINGS_AUTHORITY);
cmSettingImage.addQuery(SettingsConstants.SYSTEM, cmSystemSettingList);
cmSettingImage.addQuery(SettingsConstants.SECURE, cmSecureSettingList);
cmSettingImage.addQuery(SettingsConstants.GLOBAL, cmGlobalSettingList);
cmSettingImage.execute();
//Validate
System.out.println("\n\nValidating " + SettingsConstants.SYSTEM + "...");
validate(legacySystemSettings, cmSystemSettingList);
System.out.println("\n\nValidating " + SettingsConstants.SECURE + "...");
validate(legacySecureSettings, cmSecureSettingList);
System.out.println("\n\nValidating " + SettingsConstants.GLOBAL + "...");
validate(legacyGlobalSettings, cmGlobalSettingList);
System.exit(0);
}
private static void showUsage() {
System.err.println("Usage: MigrationTest --settings [example setting file] "
+ "--bootimg [image]"
+ "--systemimg [image]");
}
private static class Tokenizer {
private final String[] mArgs;
private int mNextArg;
public Tokenizer(String[] args) {
mArgs = args;
}
private String nextArg() {
if (mNextArg < mArgs.length) {
return mArgs[mNextArg++];
} else {
return null;
}
}
}
private static String argumentValueRequired(String argument) {
String value = tokenizer.nextArg();
if (value == null || value.length() == 0 || value.startsWith(ARGUMENT_PREFIX)) {
throw new IllegalArgumentException("No value for argument: " + argument);
}
return value;
}
private static void validate(ArrayList<Setting> legacySettings, ArrayList<Setting> cmSettings) {
Collections.sort(legacySettings);
Collections.sort(cmSettings);
if (legacySettings.size() != cmSettings.size()) {
System.err.println("Warning: Size mismatch: " + " legacy "
+ legacySettings.size() + " cm " + cmSettings.size());
}
for (int i = 0; i < legacySettings.size(); i++) {
Setting legacySetting = legacySettings.get(i);
Setting cmSetting = cmSettings.get(i);
int error = 0;
System.out.println("Comparing: legacy " + legacySetting.getKey() + " and cmsetting "
+ cmSetting.getKey());
if (!legacySetting.getKey().equals(cmSetting.getKey())) {
System.err.println(" Key mismatch: " + legacySetting.getKey() + " and "
+ cmSetting.getKey());
error = 1;
}
if (!legacySetting.getKeyType().equals(cmSetting.getKeyType())) {
System.err.println(" Key type mismatch: " + legacySetting.getKeyType() + " and "
+ cmSetting.getKeyType());
error = 1;
}
if (legacySetting.getValue().length() > 0) {
if (!legacySetting.getValue().equals(cmSetting.getValue())) {
System.err.println(" Value mismatch: " + legacySetting.getValue() + " and "
+ cmSetting.getValue());
error = 1;
}
}
if (!legacySetting.getValueType().equals(cmSetting.getValueType())) {
System.err.println(" Value type mismatch: " + legacySetting.getValueType()
+ " and " + cmSetting.getValueType());
error = 1;
}
if (error > 0) {
System.exit(-1);
} else {
System.out.println("...OK");
}
}
}
}

View File

@ -0,0 +1,110 @@
/*
* 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.
*/
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Essentially:
* adb shell content query --uri content://settings/secure --projection name:value
* --where \"name=\'new_setting\'\" --sort \"name ASC\"\n"
*/
public class QueryCommand extends Command {
private static final String[] QUERY_SETTINGS = {
"adb", "shell", "content", "query", "--uri" };
private static final String PROJECTION = "name:value";
private ArrayList<Setting> targetList;
private String targetUri;
protected QueryCommand(String targetUri,
ArrayList<Setting> targetList) {
this.targetUri = targetUri;
this.targetList = targetList;
}
@Override
public void run() {
System.out.println("\nQuerying settings for authority "
+ getAuthority() + " for target uri " + targetUri + "...");
query(targetUri, targetList);
synchronized (this) {
notifyAll();
}
}
private void query(String uri, ArrayList<Setting> arrayList) {
String[] commands = QUERY_SETTINGS;
List<String> commandList = new ArrayList<String>(
QUERY_SETTINGS.length + 1);
commandList.addAll(Arrays.asList(commands));
commandList.add(SettingsConstants.CONTENT_URI + getAuthority() + uri);
commandList.add("--projection");
commandList.add(PROJECTION);
commandList.add("--show-type"); //this is totally awesomely cm specific
commandList.add("true");
commands = commandList.toArray(new String[commandList.size()]);
if (MigrationTest.DEBUG) {
System.out.println("Using commands: " + Arrays.toString(commands));
}
try {
final Process process = Runtime.getRuntime().exec(commands);
final InputStream err = process.getErrorStream();
// Send error output to stderr.
Thread errThread = new Thread() {
@Override
public void run() {
copy(err, System.err);
}
};
errThread.setDaemon(true);
errThread.start();
BufferedReader in = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
if (!line.startsWith("Row: ")) {
throw new IOException("Unable to read settings");
}
if (MigrationTest.DEBUG) {
System.out.println("LINE: " + line);
}
Setting setting = RowParser.parseAndPopulate(true, line);
if (filter(uri, setting)) {
continue;
}
arrayList.add(setting);
}
in.close();
err.close();
process.destroy();
} catch (IOException e) {
System.err.println("Error ");
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,76 @@
/*
* 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.
*/
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
/**
* Created by adnan on 11/17/15.
*/
public class ReadCommand extends Command {
private String targetFile;
private ArrayList<Setting> targetList;
private String targetUri;
protected ReadCommand(String targetFile, String targetUri, ArrayList<Setting> targetList) {
this.targetFile = targetFile;
this.targetUri = targetUri;
this.targetList = targetList;
}
@Override
public void run() {
System.out.println("\nReading settings for authority "
+ getAuthority() + " for target uri " + targetUri + " from file "
+ targetFile +"...");
read(targetFile, targetUri, targetList);
synchronized (this) {
notifyAll();
}
}
private void read(String fileName, String uri, ArrayList<Setting> arrayList) {
try {
BufferedReader in = new BufferedReader(
new FileReader(fileName));
String line;
//Skip first two lines of header
for (int i = 0; i < 2; i++) {
in.readLine();
}
while ((line = in.readLine()) != null) {
if (!line.startsWith("Row: ")) {
throw new IOException("Unable to read settings");
}
if (MigrationTest.DEBUG) {
System.out.println("LINE: " + line);
}
Setting setting = RowParser.parseAndPopulate(false, line);
//Sanitize
if (filter(uri, setting)) {
continue;
}
arrayList.add(setting);
}
in.close();
} catch (IOException e) {
System.err.println("Error ");
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,50 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by adnan on 11/17/15.
*/
public class RowParser {
private static final String REGEX = "=(.+)";
private static Pattern p = Pattern.compile(REGEX);
public static Setting parseAndPopulate(boolean fromCursor, String line) {
Setting setting = new Setting();
String[] splitStrings = line.split(",");
for (int i = 0; i < 4; i++) {
Matcher matcher = p.matcher(splitStrings[i]);
while (matcher.find()) {
String value = matcher.group(0).replace("=", "").trim();
switch (i) {
case 0:
setting.setKey(value);
break;
case 1:
//Seriously?
if (fromCursor) {
setting.setKeyType(
Setting.SettingType.mapNumericToType(
Integer.parseInt(value)));
} else {
setting.setKeyType(value);
}
break;
case 2:
setting.setValue(value);
break;
case 3:
//Who the fuck decided to do this?
if (fromCursor) {
setting.setValueType(
Setting.SettingType.mapNumericToType(
Integer.parseInt(value)));
} else {
setting.setValueType(value);
}
break;
}
}
}
return setting;
}
}

View File

@ -0,0 +1,110 @@
/*
* 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.
*/
import java.io.Serializable;
/**
* A simple concept of a "setting" within the provider
*/
public class Setting implements Serializable, Comparable<Setting> {
private static final long serialVersionUID = 0;
private String key;
private String value;
private String keyType;
private String valueType;
public Setting() {
this.keyType = SettingType.TYPE_NULL;
this.valueType = SettingType.TYPE_NULL;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getKeyType() {
return keyType;
}
public void setKeyType(String type) {
this.keyType = type;
}
public String getValueType() {
return valueType;
}
public void setValueType(String valueType) {
this.valueType = valueType;
}
/** s - string, i - integer, f - float */
public static class SettingType {
private static final String TYPE_NULL = "NULL";
private static final String TYPE_STRING = "s";
private static final String TYPE_INTEGER = "i";
private static final String TYPE_FLOAT = "f";
private static final String TYPE_BLOB = "d";
//THIS IS FROM CURSOR.JAVA, DO NOT MODIFY
/** Value returned by {@link #getType(int)} if the specified column is null */
static final int FIELD_TYPE_NULL = 0;
/** Value returned by {@link #getType(int)} if the specified column type is integer */
static final int FIELD_TYPE_INTEGER = 1;
/** Value returned by {@link #getType(int)} if the specified column type is float */
static final int FIELD_TYPE_FLOAT = 2;
/** Value returned by {@link #getType(int)} if the specified column type is string */
static final int FIELD_TYPE_STRING = 3;
/** Value returned by {@link #getType(int)} if the specified column type is blob */
static final int FIELD_TYPE_BLOB = 4;
public static String mapNumericToType(int numeric) {
switch (numeric) {
case FIELD_TYPE_NULL:
return TYPE_NULL;
case FIELD_TYPE_STRING:
return TYPE_STRING;
case FIELD_TYPE_INTEGER:
return TYPE_INTEGER;
case FIELD_TYPE_FLOAT:
return TYPE_FLOAT;
case FIELD_TYPE_BLOB:
return TYPE_BLOB;
default:
return TYPE_NULL;
}
}
}
@Override
public int compareTo(Setting o) {
return this.key.compareTo(o.getKey());
}
}

View File

@ -0,0 +1,61 @@
/*
* 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.
*/
import java.util.ArrayList;
/**
* Created by adnan on 11/16/15.
*/
public class SettingImageCommands implements CommandExecutor {
private ArrayList<Command> commandHistory = new ArrayList<Command>();
private String authority;
public SettingImageCommands(String authority) {
this.authority = authority;
}
@Override
public void execute() {
for (Command commandWithTimeout : commandHistory) {
commandWithTimeout.run();
}
}
private void addCommand(Command commandWithTimeout) {
commandWithTimeout.prepend(authority);
commandHistory.add(commandWithTimeout);
}
public void addQuery(String uri, ArrayList<Setting> settings) {
QueryCommand queryCommand = new QueryCommand(uri, settings);
addCommand(queryCommand);
}
public void addInsert(String uri, Setting setting) {
InsertCommand insertCommand = new InsertCommand(uri, setting);
addCommand(insertCommand);
}
public void addRead(String fileName, String uri, ArrayList<Setting> settings) {
ReadCommand readCommand = new ReadCommand(fileName, uri, settings);
addCommand(readCommand);
}
public void addFastboot(int command, String[] arguments) {
FastbootCommand fastbootCommand = new FastbootCommand(command, arguments);
addCommand(fastbootCommand);
}
}

View File

@ -0,0 +1,37 @@
/*
* 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.
*/
import java.util.ArrayList;
/**
* Created by adnan on 11/16/15.
*/
public class SettingsConstants {
public static final String CMSETTINGS_AUTHORITY = "cmsettings";
public static final String SETTINGS_AUTHORITY = "settings";
public static final String CONTENT_URI = "content://";
public static final String SYSTEM = "/system";
public static final String SECURE = "/secure";
public static final String GLOBAL = "/global";
public static class Ignorables {
public static ArrayList<String> SECURE_SETTINGS = new ArrayList<String>();
static {
SECURE_SETTINGS.add(CMSettings.Secure.ADB_PORT);
}
}
}