Add "secure mode" and its components (enabled by default)
Support is added to inject a public key (from config.xml) into TWRP against which the flashable ZIP is verified. The public key is provided through /cache and thus deemed secure from non-system/root app manipulation. In contrast to the flashable ZIPs themselves which are located on the internal storage, where every app can manipulate the files. Even though the MD5 of the files is provided by the delta JSON and verified before rebooting to recovery, there is a tiny exploitable window there for a malicious app to manipulate or switch out ZIP files and have its own content flashed. If (reconstructed) ZIP signing is enabled, and injecting the public key is enabled, "secure mode" becomes available (with user override). In addition to verifying the update's cryptographic signature in recovery, this mode disables the flashing of additional ZIPs from the FlashAfterUpdate subfolder (as their origin cannot be verified in recovery and any app can manipulate these files), and also fully disables what little CWM compatibility there was (as we cannot do the verification check through CWM) Various warnings and popups have also been added to inform the user of each situation as it occurs.
This commit is contained in:
parent
e2790fdc59
commit
ac09391b5b
|
@ -39,6 +39,9 @@ fail anyway (if enabled). So to save a bit of processing, this feature is
|
|||
turned off by default. The needed files are generated and the client knows how
|
||||
to deal with them, so enabling this feature is just a configuration switch away.
|
||||
|
||||
**TODO** Update this signature documentation and "secure mode". Signature
|
||||
verification can now be enabled.
|
||||
|
||||
The produced delta files are pushed to the public download server, and the
|
||||
current build is saved to a private location to serve as input for the next
|
||||
differential run.
|
||||
|
|
|
@ -6,9 +6,16 @@
|
|||
android:showAsAction="never"
|
||||
android:title="@string/action_networks" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_secure_mode"
|
||||
android:orderInCategory="101"
|
||||
android:showAsAction="never"
|
||||
android:checkable="true"
|
||||
android:title="@string/action_secure_mode" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_about"
|
||||
android:orderInCategory="101"
|
||||
android:orderInCategory="102"
|
||||
android:showAsAction="never"
|
||||
android:title="@string/action_about" />
|
||||
|
||||
|
|
|
@ -25,5 +25,17 @@
|
|||
<string name="url_base_full">http://dl.omnirom.org/%s/</string>
|
||||
|
||||
<!-- Applies whole-file signature delta. Adds one extra delta step. Required if recovery verifies signatures -->
|
||||
<item type="bool" name="apply_signature">false</item>
|
||||
<item type="bool" name="apply_signature">true</item>
|
||||
|
||||
<!-- (TWRP) Set this to false if the keys below aren't your ROM's -->
|
||||
<item type="bool" name="inject_signature_enable">true</item>
|
||||
|
||||
<!-- (TWRP) Verification signatures to inject. Produced by 'dumpkey.jar' (out/host) of the platform.x509 key used to sign the ZIP file -->
|
||||
<string name="inject_signature_keys"><![CDATA[v2 {64,0x9d3ef4e7,{3451855145,2574857780,2212470067,2065828617,2220798544,1453138002,3035953543,349537325,3471576065,3709424322,1499657722,626083680,3508502098,135982109,2406850010,2674691998,3903782739,3673009508,3196976129,124737966,3727608735,3698514242,2926317182,2598715876,2200551045,3324466456,2027872794,1316834497,3538558575,4094723182,3091112109,152419065,961268200,2817719766,2542630774,735678394,2025086356,3319743251,3482513753,3754037486,2186326636,2162920719,1933319201,1362420666,3093979107,3944963833,3173846995,3307766644,4239176696,3380551792,3189093155,3679104199,4159403556,3373361362,737822358,2043192588,3446724037,2184123451,3680508975,492248740,1654088879,3739912969,188663922,4074712169},{2400585854,3884144496,312737665,3547448515,3596760612,2953776441,190371072,1980790627,3681130262,827104214,1597200957,1333455720,1391423898,4233042842,3284284880,50168935,2424437529,2771213818,3715896496,3320142743,3649069246,2702994054,839870558,1257344415,3116165843,4195920375,2497396347,1334871168,3550010104,64795091,3042249326,4155098628,123980023,3500559217,1825888674,443352554,3891428201,2484397377,4136956616,4201065713,2547196505,3411971111,2135688607,393830937,4198844531,3826748593,3979050977,1220766766,3592470842,2278136,1841247501,3507376964,3313320668,3849023694,2185649624,3043141327,1601153541,939583339,2083130022,3508853409,2068728550,3713282728,2402412627,1764295415}}]]></string>
|
||||
|
||||
<!-- (TWRP) Add secure mode setting. Requires 'apply_signature' and 'inject_signature_enable'. Limits flashing to TWRP only, verifies ZIP signature in TWRP, disables auto-flashing of additional ZIPs -->
|
||||
<item type="bool" name="secure_mode_enable">true</item>
|
||||
|
||||
<!-- (TWRP) Requires 'secure_mode_enable'. Decides whether the default setting for secure mode is enabled (true) or disabled (false) -->
|
||||
<item type="bool" name="secure_mode_default">true</item>
|
||||
</resources>
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="action_secure_mode">Secure</string>
|
||||
|
||||
<string name="secure_mode_enabled_title">Secure mode: enabled</string>
|
||||
<string name="secure_mode_enabled_description"><![CDATA[The update\'s cryptographic signature will be verified before flashing to confirm its origin, and no additional updates will be flashed. This mode is only compatible with <b>TWRP</b>.]]></string>
|
||||
|
||||
<string name="secure_mode_disabled_title">Secure mode: disabled</string>
|
||||
<string name="secure_mode_disabled_description"><![CDATA[Additional updates are flashed if present. Note that any (malicious) software package can place these updates and gain full system access. Caution is advised.]]></string>
|
||||
|
||||
<string name="recovery_notice_title">Notice</string>
|
||||
<string name="recovery_notice_description_secure"><![CDATA[Flashing updates in the current mode is only supported on <b>TWRP</b> recoveries. Please make sure you are running a compatible recovery before continuing.]]></string>
|
||||
<string name="recovery_notice_description_not_secure"><![CDATA[Flashing updates in the current mode is only officially supported on <b>TWRP</b> recoveries. <i>Official</i> <b>CWM</b> builds are <i>not</i> supported, though some <i>community-built</i> versions of <b>CWM</b> <i>may</i> still work. Please make sure you are running a compatible recovery before continuing.]]></string>
|
||||
|
||||
<string name="flash_after_update_notice_title">Notice</string>
|
||||
<string name="flash_after_update_notice_description"><![CDATA[Additional updates are present in the <b>FlashAfterUpdate</b> folder, but secure mode is enabled. If you continue, these additional updates will <b>not</b> be flashed.]]></string>
|
||||
</resources>
|
|
@ -22,11 +22,16 @@
|
|||
package eu.chainfire.opendelta;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Environment;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class Config {
|
||||
|
@ -38,16 +43,26 @@ public class Config {
|
|||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private final static String PREF_SECURE_MODE_NAME = "secure_mode";
|
||||
private final static String PREF_SHOWN_RECOVERY_WARNING_SECURE_NAME = "shown_recovery_warning_secure";
|
||||
private final static String PREF_SHOWN_RECOVERY_WARNING_NOT_SECURE_NAME = "shown_recovery_warning_not_secure";
|
||||
|
||||
private final SharedPreferences prefs;
|
||||
|
||||
private String property_version;
|
||||
private String property_device;
|
||||
private String filename_base;
|
||||
private String path_base;
|
||||
private String path_flash_after_update;
|
||||
private String url_base_delta;
|
||||
private String url_base_update;
|
||||
private String url_base_full;
|
||||
private boolean apply_signature;
|
||||
private final String property_version;
|
||||
private final String property_device;
|
||||
private final String filename_base;
|
||||
private final String path_base;
|
||||
private final String path_flash_after_update;
|
||||
private final String url_base_delta;
|
||||
private final String url_base_update;
|
||||
private final String url_base_full;
|
||||
private final boolean apply_signature;
|
||||
private final boolean inject_signature_enable;
|
||||
private final String inject_signature_keys;
|
||||
private final boolean secure_mode_enable;
|
||||
private final boolean secure_mode_default;
|
||||
|
||||
/*
|
||||
* Using reflection voodoo instead calling the hidden class directly, to
|
||||
|
@ -72,6 +87,8 @@ public class Config {
|
|||
}
|
||||
|
||||
private Config(Context context) {
|
||||
prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
||||
Resources res = context.getResources();
|
||||
|
||||
property_version = getProperty(context, res.getString(R.string.property_version), "");
|
||||
|
@ -94,6 +111,10 @@ public class Config {
|
|||
url_base_full = String.format(Locale.ENGLISH, res.getString(R.string.url_base_full),
|
||||
property_device);
|
||||
apply_signature = res.getBoolean(R.bool.apply_signature);
|
||||
inject_signature_enable = res.getBoolean(R.bool.inject_signature_enable);
|
||||
inject_signature_keys = res.getString(R.string.inject_signature_keys);
|
||||
secure_mode_enable = res.getBoolean(R.bool.secure_mode_enable);
|
||||
secure_mode_default = res.getBoolean(R.bool.secure_mode_default);
|
||||
|
||||
Logger.d("property_version: %s", property_version);
|
||||
Logger.d("property_device: %s", property_device);
|
||||
|
@ -104,6 +125,10 @@ public class Config {
|
|||
Logger.d("url_base_update: %s", url_base_update);
|
||||
Logger.d("url_base_full: %s", url_base_full);
|
||||
Logger.d("apply_signature: %d", apply_signature ? 1 : 0);
|
||||
Logger.d("inject_signature_enable: %d", inject_signature_enable ? 1 : 0);
|
||||
Logger.d("inject_signature_keys: %s", inject_signature_keys);
|
||||
Logger.d("secure_mode_enable: %d", secure_mode_enable ? 1 : 0);
|
||||
Logger.d("secure_mode_default: %d", secure_mode_default ? 1 : 0);
|
||||
}
|
||||
|
||||
public String getFilenameBase() {
|
||||
|
@ -133,4 +158,64 @@ public class Config {
|
|||
public boolean getApplySignature() {
|
||||
return apply_signature;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getInjectSignatureEnable() {
|
||||
return inject_signature_enable;
|
||||
}
|
||||
|
||||
public String getInjectSignatureKeys() {
|
||||
return inject_signature_keys;
|
||||
}
|
||||
|
||||
public boolean getSecureModeEnable() {
|
||||
return apply_signature && inject_signature_enable && secure_mode_enable;
|
||||
}
|
||||
|
||||
public boolean getSecureModeDefault() {
|
||||
return secure_mode_default && getSecureModeEnable();
|
||||
}
|
||||
|
||||
public boolean getSecureModeCurrent() {
|
||||
return getSecureModeEnable() && prefs.getBoolean(PREF_SECURE_MODE_NAME, getSecureModeDefault());
|
||||
}
|
||||
|
||||
public boolean setSecureModeCurrent(boolean enable) {
|
||||
prefs.edit().putBoolean(PREF_SECURE_MODE_NAME, getSecureModeEnable() && enable).commit();
|
||||
return getSecureModeCurrent();
|
||||
}
|
||||
|
||||
public List<String> getFlashAfterUpdateZIPs() {
|
||||
List<String> extras = new ArrayList<String>();
|
||||
|
||||
File[] files = (new File(getPathFlashAfterUpdate())).listFiles();
|
||||
if (files != null) {
|
||||
for (File f : files) {
|
||||
if (f.getName().toLowerCase(Locale.ENGLISH).endsWith(".zip")) {
|
||||
String filename = f.getAbsolutePath();
|
||||
if (filename.startsWith(getPathBase())) {
|
||||
extras.add(filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
Collections.sort(extras);
|
||||
}
|
||||
|
||||
return extras;
|
||||
}
|
||||
|
||||
public boolean getShownRecoveryWarningSecure() {
|
||||
return prefs.getBoolean(PREF_SHOWN_RECOVERY_WARNING_SECURE_NAME, false);
|
||||
}
|
||||
|
||||
public void setShownRecoveryWarningSecure() {
|
||||
prefs.edit().putBoolean(PREF_SHOWN_RECOVERY_WARNING_SECURE_NAME, true).commit();
|
||||
}
|
||||
|
||||
public boolean getShownRecoveryWarningNotSecure() {
|
||||
return prefs.getBoolean(PREF_SHOWN_RECOVERY_WARNING_NOT_SECURE_NAME, false);
|
||||
}
|
||||
|
||||
public void setShownRecoveryWarningNotSecure() {
|
||||
prefs.edit().putBoolean(PREF_SHOWN_RECOVERY_WARNING_NOT_SECURE_NAME, true).commit();
|
||||
}
|
||||
}
|
|
@ -53,6 +53,8 @@ public class MainActivity extends Activity {
|
|||
private ProgressBar progress = null;
|
||||
private Button checkNow = null;
|
||||
private Button flashNow = null;
|
||||
|
||||
private Config config;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -76,11 +78,20 @@ public class MainActivity extends Activity {
|
|||
progress = (ProgressBar) findViewById(R.id.progress);
|
||||
checkNow = (Button) findViewById(R.id.button_check_now);
|
||||
flashNow = (Button) findViewById(R.id.button_flash_now);
|
||||
|
||||
config = Config.getInstance(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.main, menu);
|
||||
|
||||
if (!config.getSecureModeEnable()) {
|
||||
menu.findItem(R.id.action_secure_mode).setVisible(false);
|
||||
} else {
|
||||
menu.findItem(R.id.action_secure_mode).setChecked(config.getSecureModeCurrent());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -170,6 +181,17 @@ public class MainActivity extends Activity {
|
|||
return true;
|
||||
case R.id.action_networks:
|
||||
showNetworks();
|
||||
return true;
|
||||
case R.id.action_secure_mode:
|
||||
item.setChecked(config.setSecureModeCurrent(!item.isChecked()));
|
||||
|
||||
(new AlertDialog.Builder(this)).
|
||||
setTitle(item.isChecked() ? R.string.secure_mode_enabled_title : R.string.secure_mode_disabled_title).
|
||||
setMessage(Html.fromHtml(getString(item.isChecked() ? R.string.secure_mode_enabled_description : R.string.secure_mode_disabled_description))).
|
||||
setCancelable(true).
|
||||
setNeutralButton(android.R.string.ok, null).
|
||||
show();
|
||||
|
||||
return true;
|
||||
case R.id.action_about:
|
||||
showAbout();
|
||||
|
@ -311,8 +333,78 @@ public class MainActivity extends Activity {
|
|||
}
|
||||
|
||||
public void onButtonFlashNowClick(View v) {
|
||||
UpdateService.startFlash(this);
|
||||
checkNow.setEnabled(false);
|
||||
flashNow.setEnabled(false);
|
||||
flashRecoveryWarning.run();
|
||||
}
|
||||
|
||||
private Runnable flashRecoveryWarning = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Show a warning message about recoveries we support, depending
|
||||
// on the state of secure mode and if we've shown the message before
|
||||
|
||||
final Runnable next = flashWarningFlashAfterUpdateZIPs;
|
||||
|
||||
CharSequence message = null;
|
||||
if (!config.getSecureModeCurrent() && !config.getShownRecoveryWarningNotSecure()) {
|
||||
message = Html.fromHtml(getString(R.string.recovery_notice_description_not_secure));
|
||||
config.setShownRecoveryWarningNotSecure();
|
||||
} else if (config.getSecureModeCurrent() && !config.getShownRecoveryWarningSecure()) {
|
||||
message = Html.fromHtml(getString(R.string.recovery_notice_description_secure));
|
||||
config.setShownRecoveryWarningSecure();
|
||||
}
|
||||
|
||||
if (message != null) {
|
||||
(new AlertDialog.Builder(MainActivity.this)).
|
||||
setTitle(R.string.recovery_notice_title).
|
||||
setMessage(message).
|
||||
setCancelable(true).
|
||||
setNegativeButton(android.R.string.cancel, null).
|
||||
setPositiveButton(android.R.string.ok, new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
next.run();
|
||||
}
|
||||
}).
|
||||
show();
|
||||
} else {
|
||||
next.run();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private Runnable flashWarningFlashAfterUpdateZIPs = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// If we're in secure mode, but additional ZIPs to flash have been
|
||||
// detected, warn the user that these will not be flashed
|
||||
|
||||
final Runnable next = flashStart;
|
||||
|
||||
if (config.getSecureModeCurrent() && (config.getFlashAfterUpdateZIPs().size() > 0)) {
|
||||
(new AlertDialog.Builder(MainActivity.this)).
|
||||
setTitle(R.string.flash_after_update_notice_title).
|
||||
setMessage(Html.fromHtml(getString(R.string.flash_after_update_notice_description))).
|
||||
setCancelable(true).
|
||||
setNegativeButton(android.R.string.cancel, null).
|
||||
setPositiveButton(android.R.string.ok, new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
next.run();
|
||||
}
|
||||
}).
|
||||
show();
|
||||
} else {
|
||||
next.run();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private Runnable flashStart = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
checkNow.setEnabled(false);
|
||||
flashNow.setEnabled(false);
|
||||
UpdateService.startFlash(MainActivity.this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -58,13 +58,15 @@ import org.json.JSONException;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigInteger;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
|
@ -102,7 +104,7 @@ public class UpdateService
|
|||
intent.putExtra(EXTRA_ALARM_ID, id);
|
||||
return PendingIntent.getService(context, id, intent, 0);
|
||||
}
|
||||
|
||||
|
||||
public static final String ACTION_SYSTEM_UPDATE_SETTINGS = "android.settings.SYSTEM_UPDATE_SETTINGS";
|
||||
public static final String PERMISSION_ACCESS_CACHE_FILESYSTEM = "android.permission.ACCESS_CACHE_FILESYSTEM";
|
||||
public static final String PERMISSION_REBOOT = "android.permission.REBOOT";
|
||||
|
@ -924,6 +926,10 @@ public class UpdateService
|
|||
return true;
|
||||
}
|
||||
|
||||
private void writeString(OutputStream os, String s) throws UnsupportedEncodingException, IOException {
|
||||
os.write((s + "\n").getBytes("UTF-8"));
|
||||
}
|
||||
|
||||
@SuppressLint("SdCardPath")
|
||||
private void flashUpdate() {
|
||||
if (getPackageManager().checkPermission(PERMISSION_ACCESS_CACHE_FILESYSTEM,
|
||||
|
@ -948,46 +954,62 @@ public class UpdateService
|
|||
String path_sd = Environment.getExternalStorageDirectory() + File.separator;
|
||||
flashFilename = flashFilename.substring(path_sd.length());
|
||||
|
||||
// Find additional ZIPs to flash
|
||||
List<String> extras = new ArrayList<String>();
|
||||
{
|
||||
File[] files = (new File(config.getPathFlashAfterUpdate())).listFiles();
|
||||
if (files != null) {
|
||||
for (File f : files) {
|
||||
if (f.getName().toLowerCase(Locale.ENGLISH).endsWith(".zip")) {
|
||||
String filename = f.getAbsolutePath();
|
||||
if (filename.startsWith(config.getPathBase())) {
|
||||
extras.add(filename.substring(path_sd.length()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Collections.sort(extras);
|
||||
// Find additional ZIPs to flash, strip path to sd
|
||||
List<String> extras = config.getFlashAfterUpdateZIPs();
|
||||
for (int i = 0; i < extras.size(); i++) {
|
||||
extras.set(i, extras.get(i).substring(path_sd.length()));
|
||||
}
|
||||
|
||||
try {
|
||||
// We're using TWRP's openrecoveryscript as primary, and CWM's
|
||||
// extendedcommand as fallback. Using AOSP's command would
|
||||
// break older TWRPs. extendedcommand is broken on 'official'
|
||||
// CWM builds, though.
|
||||
|
||||
// TWRP - OpenRecoveryScript - the recovery will find the correct
|
||||
// storage root for the ZIPs,
|
||||
// life is nice and easy.
|
||||
if ((flashFilename != null) && (!flashFilename.equals(""))) {
|
||||
// storage root for the ZIPs, life is nice and easy.
|
||||
//
|
||||
// Optionally, we're injecting our own signature verification keys
|
||||
// and verifying against those. We place these keys in /cache
|
||||
// where only privileged apps can edit, contrary to the storage
|
||||
// location of the ZIP itself - anyone can modify the ZIP.
|
||||
// As such, flashing the ZIP without checking the whole-file
|
||||
// signature coming from a secure location would be a security
|
||||
// risk.
|
||||
{
|
||||
if (config.getInjectSignatureEnable()) {
|
||||
FileOutputStream os = new FileOutputStream("/cache/recovery/keys", false);
|
||||
try {
|
||||
writeString(os, config.getInjectSignatureKeys());
|
||||
} finally {
|
||||
os.close();
|
||||
}
|
||||
setPermissions("/cache/recovery/keys", 0644, Process.myUid(), 2001 /* AID_CACHE */);
|
||||
}
|
||||
|
||||
FileOutputStream os = new FileOutputStream("/cache/recovery/openrecoveryscript",
|
||||
false);
|
||||
try {
|
||||
os.write(String.format("install %s\n", flashFilename).getBytes("UTF-8"));
|
||||
for (String file : extras) {
|
||||
os.write(String.format("install %s\n", file).getBytes("UTF-8"));
|
||||
if (config.getInjectSignatureEnable()) {
|
||||
writeString(os, "cmd cat /res/keys > /res/keys_org");
|
||||
writeString(os, "cmd cat /cache/recovery/keys > /res/keys");
|
||||
writeString(os, "set tw_signed_zip_verify 1");
|
||||
writeString(os, String.format("install %s", flashFilename));
|
||||
writeString(os, "set tw_signed_zip_verify 0");
|
||||
writeString(os, "cmd cat /res/keys_org > /res/keys");
|
||||
writeString(os, "cmd rm /res/keys_org");
|
||||
} else {
|
||||
writeString(os, String.format("install %s", flashFilename));
|
||||
}
|
||||
os.write(("wipe cache\n").getBytes("UTF-8"));
|
||||
|
||||
if (!config.getSecureModeCurrent()) {
|
||||
// any program could have placed these ZIPs, so ignore them in secure mode
|
||||
for (String file : extras) {
|
||||
writeString(os, String.format("install %s", file));
|
||||
}
|
||||
}
|
||||
writeString(os, "wipe cache");
|
||||
} finally {
|
||||
os.close();
|
||||
}
|
||||
|
||||
setPermissions("/cache/recovery/openrecoveryscript", 0644, Process.myUid(), 2001 /* AID_CACHE */);
|
||||
}
|
||||
setPermissions("/cache/recovery/openrecoveryscript", 0644, Process.myUid(), 2001 /* AID_CACHE */);
|
||||
|
||||
// CWM - ExtendedCommand - provide paths to both internal and
|
||||
// external storage locations, it's nigh impossible to know in
|
||||
|
@ -997,31 +1019,31 @@ public class UpdateService
|
|||
// versions to have them reversed. It'll give some horrible looking
|
||||
// results, but it seems to continue installing even if one ZIP
|
||||
// fails and produce the wanted result. Better than nothing ...
|
||||
if ((flashFilename != null) && (!flashFilename.equals(""))) {
|
||||
//
|
||||
// We don't generate a CWM script in secure mode, because it
|
||||
// doesn't support checking our custom signatures
|
||||
if (!config.getSecureModeCurrent()) {
|
||||
FileOutputStream os = new FileOutputStream("/cache/recovery/extendedcommand", false);
|
||||
try {
|
||||
os.write(String.format("install_zip(\"%s%s\");\n", "/sdcard/", flashFilename)
|
||||
.getBytes("UTF-8"));
|
||||
os.write(String.format("install_zip(\"%s%s\");\n", "/emmc/", flashFilename)
|
||||
.getBytes("UTF-8"));
|
||||
writeString(os, String.format("install_zip(\"%s%s\");", "/sdcard/", flashFilename));
|
||||
writeString(os, String.format("install_zip(\"%s%s\");", "/emmc/", flashFilename));
|
||||
for (String file : extras) {
|
||||
os.write(String.format("install_zip(\"%s%s\");\n", "/sdcard/", file)
|
||||
.getBytes("UTF-8"));
|
||||
os.write(String.format("install_zip(\"%s%s\");\n", "/emmc/", file)
|
||||
.getBytes("UTF-8"));
|
||||
writeString(os, String.format("install_zip(\"%s%s\");", "/sdcard/", file));
|
||||
writeString(os, String.format("install_zip(\"%s%s\");", "/emmc/", file));
|
||||
}
|
||||
os.write(("run_program(\"/sbin/busybox\", \"rm\", \"-rf\", \"/cache/*\");\n")
|
||||
.getBytes("UTF-8"));
|
||||
writeString(os, "run_program(\"/sbin/busybox\", \"rm\", \"-rf\", \"/cache/*\");");
|
||||
} finally {
|
||||
os.close();
|
||||
}
|
||||
|
||||
setPermissions("/cache/recovery/extendedcommand", 0644, Process.myUid(), 2001 /* AID_CACHE */);
|
||||
} else {
|
||||
(new File("/cache/recovery/extendedcommand")).delete();
|
||||
}
|
||||
setPermissions("/cache/recovery/extendedcommand", 0644, Process.myUid(), 2001 /* AID_CACHE */);
|
||||
|
||||
((PowerManager) getSystemService(Context.POWER_SERVICE)).reboot("recovery");
|
||||
} catch (Exception e) {
|
||||
// We have failed to write something. There's not really anything
|
||||
// else to do at
|
||||
// We have failed to write something. There's not really anything else to do at
|
||||
// at this stage than give up. No reason to crash though.
|
||||
Logger.ex(e);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue