Merge "updater_sample: add switch slot demo"
This commit is contained in:
commit
2ddc54f5fd
11 changed files with 210 additions and 16 deletions
|
@ -44,6 +44,10 @@ saved uncompressed (`ZIP_STORED`), so that their data can be downloaded directly
|
||||||
with the offset and length. As `payload.bin` itself is already in compressed
|
with the offset and length. As `payload.bin` itself is already in compressed
|
||||||
format, the size penalty is marginal.
|
format, the size penalty is marginal.
|
||||||
|
|
||||||
|
if `ab_config.force_switch_slot` set true device will boot to the
|
||||||
|
updated partition on next reboot; otherwise button "Switch Slot" will
|
||||||
|
become active, and user can manually set updated partition as the active slot.
|
||||||
|
|
||||||
Config files can be generated using `tools/gen_update_config.py`.
|
Config files can be generated using `tools/gen_update_config.py`.
|
||||||
Running `./tools/gen_update_config.py --help` shows usage of the script.
|
Running `./tools/gen_update_config.py --help` shows usage of the script.
|
||||||
|
|
||||||
|
@ -85,8 +89,8 @@ which HTTP headers are supported.
|
||||||
- [x] Add stop/reset the update
|
- [x] Add stop/reset the update
|
||||||
- [x] Add demo for passing HTTP headers to `UpdateEngine#applyPayload`
|
- [x] Add demo for passing HTTP headers to `UpdateEngine#applyPayload`
|
||||||
- [x] [Package compatibility check](https://source.android.com/devices/architecture/vintf/match-rules)
|
- [x] [Package compatibility check](https://source.android.com/devices/architecture/vintf/match-rules)
|
||||||
|
- [x] Deferred switch slot demo
|
||||||
- [ ] Add tests for `MainActivity`
|
- [ ] Add tests for `MainActivity`
|
||||||
- [ ] Change partition demo
|
|
||||||
- [ ] Verify system partition checksum for package
|
- [ ] Verify system partition checksum for package
|
||||||
- [ ] Add non-A/B updates demo
|
- [ ] Add non-A/B updates demo
|
||||||
|
|
||||||
|
|
|
@ -178,6 +178,23 @@
|
||||||
android:text="Reset" />
|
android:text="Reset" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textViewUpdateInfo"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="14dp"
|
||||||
|
android:textColor="#777"
|
||||||
|
android:textSize="10sp"
|
||||||
|
android:textStyle="italic"
|
||||||
|
android:text="@string/finish_update_info" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/buttonSwitchSlot"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:onClick="onSwitchSlotClick"
|
||||||
|
android:text="@string/switch_slot" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
|
@ -20,5 +20,10 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"authorization": "Basic my-secret-token"
|
"authorization": "Basic my-secret-token"
|
||||||
|
},
|
||||||
|
"ab_config": {
|
||||||
|
"__": "A/B (seamless) update configurations",
|
||||||
|
"__force_switch_slot": "if set true device will boot to a new slot, otherwise user manually switches slot on the screen",
|
||||||
|
"force_switch_slot": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,4 +18,6 @@
|
||||||
<string name="action_reload">Reload</string>
|
<string name="action_reload">Reload</string>
|
||||||
<string name="unknown">Unknown</string>
|
<string name="unknown">Unknown</string>
|
||||||
<string name="close">CLOSE</string>
|
<string name="close">CLOSE</string>
|
||||||
|
<string name="switch_slot">Switch Slot</string>
|
||||||
|
<string name="finish_update_info">To finish the update press the button below</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -71,7 +71,7 @@ public class UpdateConfig implements Parcelable {
|
||||||
JSONObject meta = o.getJSONObject("ab_streaming_metadata");
|
JSONObject meta = o.getJSONObject("ab_streaming_metadata");
|
||||||
JSONArray propertyFilesJson = meta.getJSONArray("property_files");
|
JSONArray propertyFilesJson = meta.getJSONArray("property_files");
|
||||||
PackageFile[] propertyFiles =
|
PackageFile[] propertyFiles =
|
||||||
new PackageFile[propertyFilesJson.length()];
|
new PackageFile[propertyFilesJson.length()];
|
||||||
for (int i = 0; i < propertyFilesJson.length(); i++) {
|
for (int i = 0; i < propertyFilesJson.length(); i++) {
|
||||||
JSONObject p = propertyFilesJson.getJSONObject(i);
|
JSONObject p = propertyFilesJson.getJSONObject(i);
|
||||||
propertyFiles[i] = new PackageFile(
|
propertyFiles[i] = new PackageFile(
|
||||||
|
@ -87,6 +87,12 @@ public class UpdateConfig implements Parcelable {
|
||||||
propertyFiles,
|
propertyFiles,
|
||||||
authorization);
|
authorization);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: parse only for A/B updates when non-A/B is implemented
|
||||||
|
JSONObject ab = o.getJSONObject("ab_config");
|
||||||
|
boolean forceSwitchSlot = ab.getBoolean("force_switch_slot");
|
||||||
|
c.mAbConfig = new AbConfig(forceSwitchSlot);
|
||||||
|
|
||||||
c.mRawJson = json;
|
c.mRawJson = json;
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
@ -109,6 +115,9 @@ public class UpdateConfig implements Parcelable {
|
||||||
/** metadata is required only for streaming update */
|
/** metadata is required only for streaming update */
|
||||||
private StreamingMetadata mAbStreamingMetadata;
|
private StreamingMetadata mAbStreamingMetadata;
|
||||||
|
|
||||||
|
/** A/B update configurations */
|
||||||
|
private AbConfig mAbConfig;
|
||||||
|
|
||||||
private String mRawJson;
|
private String mRawJson;
|
||||||
|
|
||||||
protected UpdateConfig() {
|
protected UpdateConfig() {
|
||||||
|
@ -119,6 +128,7 @@ public class UpdateConfig implements Parcelable {
|
||||||
this.mUrl = in.readString();
|
this.mUrl = in.readString();
|
||||||
this.mAbInstallType = in.readInt();
|
this.mAbInstallType = in.readInt();
|
||||||
this.mAbStreamingMetadata = (StreamingMetadata) in.readSerializable();
|
this.mAbStreamingMetadata = (StreamingMetadata) in.readSerializable();
|
||||||
|
this.mAbConfig = (AbConfig) in.readSerializable();
|
||||||
this.mRawJson = in.readString();
|
this.mRawJson = in.readString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,6 +158,10 @@ public class UpdateConfig implements Parcelable {
|
||||||
return mAbStreamingMetadata;
|
return mAbStreamingMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AbConfig getAbConfig() {
|
||||||
|
return mAbConfig;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return File object for given url
|
* @return File object for given url
|
||||||
*/
|
*/
|
||||||
|
@ -172,6 +186,7 @@ public class UpdateConfig implements Parcelable {
|
||||||
dest.writeString(mUrl);
|
dest.writeString(mUrl);
|
||||||
dest.writeInt(mAbInstallType);
|
dest.writeInt(mAbInstallType);
|
||||||
dest.writeSerializable(mAbStreamingMetadata);
|
dest.writeSerializable(mAbStreamingMetadata);
|
||||||
|
dest.writeSerializable(mAbConfig);
|
||||||
dest.writeString(mRawJson);
|
dest.writeString(mRawJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,9 +200,11 @@ public class UpdateConfig implements Parcelable {
|
||||||
/** defines beginning of update data in archive */
|
/** defines beginning of update data in archive */
|
||||||
private PackageFile[] mPropertyFiles;
|
private PackageFile[] mPropertyFiles;
|
||||||
|
|
||||||
/** SystemUpdaterSample receives the authorization token from the OTA server, in addition
|
/**
|
||||||
|
* SystemUpdaterSample receives the authorization token from the OTA server, in addition
|
||||||
* to the package URL. It passes on the info to update_engine, so that the latter can
|
* to the package URL. It passes on the info to update_engine, so that the latter can
|
||||||
* fetch the data from the package server directly with the token. */
|
* fetch the data from the package server directly with the token.
|
||||||
|
*/
|
||||||
private String mAuthorization;
|
private String mAuthorization;
|
||||||
|
|
||||||
public StreamingMetadata(PackageFile[] propertyFiles, String authorization) {
|
public StreamingMetadata(PackageFile[] propertyFiles, String authorization) {
|
||||||
|
@ -239,4 +256,27 @@ public class UpdateConfig implements Parcelable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
/**
|
||||||
|
* A/B (seamless) update configurations.
|
||||||
|
*/
|
||||||
|
public static class AbConfig implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 31044L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* if set true device will boot to new slot, otherwise user manually
|
||||||
|
* switches slot on the screen.
|
||||||
|
*/
|
||||||
|
private boolean mForceSwitchSlot;
|
||||||
|
|
||||||
|
public AbConfig(boolean forceSwitchSlot) {
|
||||||
|
this.mForceSwitchSlot = forceSwitchSlot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getForceSwitchSlot() {
|
||||||
|
return mForceSwitchSlot;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -18,6 +18,7 @@ package com.example.android.systemupdatersample.ui;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
|
import android.graphics.Color;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.UpdateEngine;
|
import android.os.UpdateEngine;
|
||||||
|
@ -38,11 +39,13 @@ import com.example.android.systemupdatersample.services.PrepareStreamingService;
|
||||||
import com.example.android.systemupdatersample.util.PayloadSpecs;
|
import com.example.android.systemupdatersample.util.PayloadSpecs;
|
||||||
import com.example.android.systemupdatersample.util.UpdateConfigs;
|
import com.example.android.systemupdatersample.util.UpdateConfigs;
|
||||||
import com.example.android.systemupdatersample.util.UpdateEngineErrorCodes;
|
import com.example.android.systemupdatersample.util.UpdateEngineErrorCodes;
|
||||||
|
import com.example.android.systemupdatersample.util.UpdateEngineProperties;
|
||||||
import com.example.android.systemupdatersample.util.UpdateEngineStatuses;
|
import com.example.android.systemupdatersample.util.UpdateEngineStatuses;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,10 +69,14 @@ public class MainActivity extends Activity {
|
||||||
private ProgressBar mProgressBar;
|
private ProgressBar mProgressBar;
|
||||||
private TextView mTextViewStatus;
|
private TextView mTextViewStatus;
|
||||||
private TextView mTextViewCompletion;
|
private TextView mTextViewCompletion;
|
||||||
|
private TextView mTextViewUpdateInfo;
|
||||||
|
private Button mButtonSwitchSlot;
|
||||||
|
|
||||||
private List<UpdateConfig> mConfigs;
|
private List<UpdateConfig> mConfigs;
|
||||||
private AtomicInteger mUpdateEngineStatus =
|
private AtomicInteger mUpdateEngineStatus =
|
||||||
new AtomicInteger(UpdateEngine.UpdateStatusConstants.IDLE);
|
new AtomicInteger(UpdateEngine.UpdateStatusConstants.IDLE);
|
||||||
|
private PayloadSpec mLastPayloadSpec;
|
||||||
|
private AtomicBoolean mManualSwitchSlotRequired = new AtomicBoolean(true);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listen to {@code update_engine} events.
|
* Listen to {@code update_engine} events.
|
||||||
|
@ -93,6 +100,8 @@ public class MainActivity extends Activity {
|
||||||
this.mProgressBar = findViewById(R.id.progressBar);
|
this.mProgressBar = findViewById(R.id.progressBar);
|
||||||
this.mTextViewStatus = findViewById(R.id.textViewStatus);
|
this.mTextViewStatus = findViewById(R.id.textViewStatus);
|
||||||
this.mTextViewCompletion = findViewById(R.id.textViewCompletion);
|
this.mTextViewCompletion = findViewById(R.id.textViewCompletion);
|
||||||
|
this.mTextViewUpdateInfo = findViewById(R.id.textViewUpdateInfo);
|
||||||
|
this.mButtonSwitchSlot = findViewById(R.id.buttonSwitchSlot);
|
||||||
|
|
||||||
this.mTextViewConfigsDirHint.setText(UpdateConfigs.getConfigsRoot(this));
|
this.mTextViewConfigsDirHint.setText(UpdateConfigs.getConfigsRoot(this));
|
||||||
|
|
||||||
|
@ -172,6 +181,13 @@ public class MainActivity extends Activity {
|
||||||
.setNegativeButton(android.R.string.cancel, null).show();
|
.setNegativeButton(android.R.string.cancel, null).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* switch slot button clicked
|
||||||
|
*/
|
||||||
|
public void onSwitchSlotClick(View view) {
|
||||||
|
setSwitchSlotOnReboot();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked when anything changes. The value of {@code status} will
|
* Invoked when anything changes. The value of {@code status} will
|
||||||
* be one of the values from {@link UpdateEngine.UpdateStatusConstants},
|
* be one of the values from {@link UpdateEngine.UpdateStatusConstants},
|
||||||
|
@ -185,16 +201,16 @@ public class MainActivity extends Activity {
|
||||||
Log.e("UpdateEngine", "StatusUpdate - status="
|
Log.e("UpdateEngine", "StatusUpdate - status="
|
||||||
+ UpdateEngineStatuses.getStatusText(status)
|
+ UpdateEngineStatuses.getStatusText(status)
|
||||||
+ "/" + status);
|
+ "/" + status);
|
||||||
setUiStatus(status);
|
|
||||||
Toast.makeText(this, "Update Status changed", Toast.LENGTH_LONG)
|
Toast.makeText(this, "Update Status changed", Toast.LENGTH_LONG)
|
||||||
.show();
|
.show();
|
||||||
if (status != UpdateEngine.UpdateStatusConstants.IDLE) {
|
if (status == UpdateEngine.UpdateStatusConstants.IDLE) {
|
||||||
Log.d(TAG, "status changed, setting ui to updating mode");
|
|
||||||
uiSetUpdating();
|
|
||||||
} else {
|
|
||||||
Log.d(TAG, "status changed, resetting ui");
|
Log.d(TAG, "status changed, resetting ui");
|
||||||
uiReset();
|
uiReset();
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "status changed, setting ui to updating mode");
|
||||||
|
uiSetUpdating();
|
||||||
}
|
}
|
||||||
|
setUiStatus(status);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -215,6 +231,13 @@ public class MainActivity extends Activity {
|
||||||
+ " " + state);
|
+ " " + state);
|
||||||
Toast.makeText(this, "Update completed", Toast.LENGTH_LONG).show();
|
Toast.makeText(this, "Update completed", Toast.LENGTH_LONG).show();
|
||||||
setUiCompletion(errorCode);
|
setUiCompletion(errorCode);
|
||||||
|
if (errorCode == UpdateEngineErrorCodes.UPDATED_BUT_NOT_ACTIVE) {
|
||||||
|
// if update was successfully applied.
|
||||||
|
if (mManualSwitchSlotRequired.get()) {
|
||||||
|
// Show "Switch Slot" button.
|
||||||
|
uiShowSwitchSlotInfo();
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,6 +254,7 @@ public class MainActivity extends Activity {
|
||||||
mProgressBar.setVisibility(ProgressBar.INVISIBLE);
|
mProgressBar.setVisibility(ProgressBar.INVISIBLE);
|
||||||
mTextViewStatus.setText(R.string.unknown);
|
mTextViewStatus.setText(R.string.unknown);
|
||||||
mTextViewCompletion.setText(R.string.unknown);
|
mTextViewCompletion.setText(R.string.unknown);
|
||||||
|
uiHideSwitchSlotInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** sets ui updating mode */
|
/** sets ui updating mode */
|
||||||
|
@ -245,6 +269,16 @@ public class MainActivity extends Activity {
|
||||||
mProgressBar.setVisibility(ProgressBar.VISIBLE);
|
mProgressBar.setVisibility(ProgressBar.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void uiShowSwitchSlotInfo() {
|
||||||
|
mButtonSwitchSlot.setEnabled(true);
|
||||||
|
mTextViewUpdateInfo.setTextColor(Color.parseColor("#777777"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void uiHideSwitchSlotInfo() {
|
||||||
|
mTextViewUpdateInfo.setTextColor(Color.parseColor("#AAAAAA"));
|
||||||
|
mButtonSwitchSlot.setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* loads json configurations from configs dir that is defined in {@link UpdateConfigs}.
|
* loads json configurations from configs dir that is defined in {@link UpdateConfigs}.
|
||||||
*/
|
*/
|
||||||
|
@ -290,6 +324,17 @@ public class MainActivity extends Activity {
|
||||||
* Applies the given update
|
* Applies the given update
|
||||||
*/
|
*/
|
||||||
private void applyUpdate(final UpdateConfig config) {
|
private void applyUpdate(final UpdateConfig config) {
|
||||||
|
List<String> extraProperties = new ArrayList<>();
|
||||||
|
|
||||||
|
if (!config.getAbConfig().getForceSwitchSlot()) {
|
||||||
|
// Disable switch slot on reboot, which is enabled by default.
|
||||||
|
// User will enable it manually by clicking "Switch Slot" button on the screen.
|
||||||
|
extraProperties.add(UpdateEngineProperties.PROPERTY_DISABLE_SWITCH_SLOT_ON_REBOOT);
|
||||||
|
mManualSwitchSlotRequired.set(true);
|
||||||
|
} else {
|
||||||
|
mManualSwitchSlotRequired.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
if (config.getInstallType() == UpdateConfig.AB_INSTALL_TYPE_NON_STREAMING) {
|
if (config.getInstallType() == UpdateConfig.AB_INSTALL_TYPE_NON_STREAMING) {
|
||||||
PayloadSpec payload;
|
PayloadSpec payload;
|
||||||
try {
|
try {
|
||||||
|
@ -300,12 +345,11 @@ public class MainActivity extends Activity {
|
||||||
.show();
|
.show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
updateEngineApplyPayload(payload, null);
|
updateEngineApplyPayload(payload, extraProperties);
|
||||||
} else {
|
} else {
|
||||||
Log.d(TAG, "Starting PrepareStreamingService");
|
Log.d(TAG, "Starting PrepareStreamingService");
|
||||||
PrepareStreamingService.startService(this, config, (code, payloadSpec) -> {
|
PrepareStreamingService.startService(this, config, (code, payloadSpec) -> {
|
||||||
if (code == PrepareStreamingService.RESULT_CODE_SUCCESS) {
|
if (code == PrepareStreamingService.RESULT_CODE_SUCCESS) {
|
||||||
List<String> extraProperties = new ArrayList<>();
|
|
||||||
extraProperties.add("USER_AGENT=" + HTTP_USER_AGENT);
|
extraProperties.add("USER_AGENT=" + HTTP_USER_AGENT);
|
||||||
config.getStreamingMetadata()
|
config.getStreamingMetadata()
|
||||||
.getAuthorization()
|
.getAuthorization()
|
||||||
|
@ -332,6 +376,8 @@ public class MainActivity extends Activity {
|
||||||
* @param extraProperties additional properties to pass to {@link UpdateEngine#applyPayload}
|
* @param extraProperties additional properties to pass to {@link UpdateEngine#applyPayload}
|
||||||
*/
|
*/
|
||||||
private void updateEngineApplyPayload(PayloadSpec payloadSpec, List<String> extraProperties) {
|
private void updateEngineApplyPayload(PayloadSpec payloadSpec, List<String> extraProperties) {
|
||||||
|
mLastPayloadSpec = payloadSpec;
|
||||||
|
|
||||||
ArrayList<String> properties = new ArrayList<>(payloadSpec.getProperties());
|
ArrayList<String> properties = new ArrayList<>(payloadSpec.getProperties());
|
||||||
if (extraProperties != null) {
|
if (extraProperties != null) {
|
||||||
properties.addAll(extraProperties);
|
properties.addAll(extraProperties);
|
||||||
|
@ -351,6 +397,29 @@ public class MainActivity extends Activity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the new slot that has the updated partitions as the active slot,
|
||||||
|
* which device will boot into next time.
|
||||||
|
* This method is only supposed to be called after the payload is applied.
|
||||||
|
*
|
||||||
|
* Invoking {@link UpdateEngine#applyPayload} with the same payload url, offset, size
|
||||||
|
* and payload metadata headers doesn't trigger new update. It can be used to just switch
|
||||||
|
* active A/B slot.
|
||||||
|
*
|
||||||
|
* {@link UpdateEngine#applyPayload} might take several seconds to finish, and it will
|
||||||
|
* invoke callbacks {@link this#onStatusUpdate} and {@link this#onPayloadApplicationComplete)}.
|
||||||
|
*/
|
||||||
|
private void setSwitchSlotOnReboot() {
|
||||||
|
Log.d(TAG, "setSwitchSlotOnReboot invoked");
|
||||||
|
List<String> extraProperties = new ArrayList<>();
|
||||||
|
// PROPERTY_SKIP_POST_INSTALL should be passed on to skip post-installation hooks.
|
||||||
|
extraProperties.add(UpdateEngineProperties.PROPERTY_SKIP_POST_INSTALL);
|
||||||
|
// It sets property SWITCH_SLOT_ON_REBOOT=1 by default.
|
||||||
|
// HTTP headers are not required, UpdateEngine is not expected to stream payload.
|
||||||
|
updateEngineApplyPayload(mLastPayloadSpec, extraProperties);
|
||||||
|
uiHideSwitchSlotInfo();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests update engine to stop any ongoing update. If an update has been applied,
|
* Requests update engine to stop any ongoing update. If an update has been applied,
|
||||||
* leave it as is.
|
* leave it as is.
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.example.android.systemupdatersample.util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for properties that will be passed to {@code UpdateEngine#applyPayload}.
|
||||||
|
*/
|
||||||
|
public final class UpdateEngineProperties {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The property indicating that the update engine should not switch slot
|
||||||
|
* when the device reboots.
|
||||||
|
*/
|
||||||
|
public static final String PROPERTY_DISABLE_SWITCH_SLOT_ON_REBOOT = "SWITCH_SLOT_ON_REBOOT=0";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The property to skip post-installation.
|
||||||
|
* https://source.android.com/devices/tech/ota/ab/#post-installation
|
||||||
|
*/
|
||||||
|
public static final String PROPERTY_SKIP_POST_INSTALL = "RUN_POST_INSTALL=0";
|
||||||
|
|
||||||
|
private UpdateEngineProperties() {}
|
||||||
|
}
|
|
@ -10,5 +10,8 @@
|
||||||
"size": 8
|
"size": 8
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"ab_config": {
|
||||||
|
"force_switch_slot": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
{
|
{
|
||||||
"__": "*** Generated using tools/gen_update_config.py ***",
|
"__": "*** Generated using tools/gen_update_config.py ***",
|
||||||
|
"ab_config": {
|
||||||
|
"force_switch_slot": false
|
||||||
|
},
|
||||||
"ab_install_type": "STREAMING",
|
"ab_install_type": "STREAMING",
|
||||||
"ab_streaming_metadata": {
|
"ab_streaming_metadata": {
|
||||||
"property_files": [
|
"property_files": [
|
||||||
|
|
|
@ -18,6 +18,7 @@ package com.example.android.systemupdatersample;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertSame;
|
import static org.junit.Assert.assertSame;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.test.InstrumentationRegistry;
|
import android.support.test.InstrumentationRegistry;
|
||||||
|
@ -45,7 +46,8 @@ public class UpdateConfigTest {
|
||||||
|
|
||||||
private static final String JSON_NON_STREAMING =
|
private static final String JSON_NON_STREAMING =
|
||||||
"{\"name\": \"vip update\", \"url\": \"file:///builds/a.zip\", "
|
"{\"name\": \"vip update\", \"url\": \"file:///builds/a.zip\", "
|
||||||
+ " \"ab_install_type\": \"NON_STREAMING\"}";
|
+ " \"ab_install_type\": \"NON_STREAMING\","
|
||||||
|
+ " \"ab_config\": { \"force_switch_slot\": false } }";
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public final ExpectedException thrown = ExpectedException.none();
|
public final ExpectedException thrown = ExpectedException.none();
|
||||||
|
@ -82,6 +84,7 @@ public class UpdateConfigTest {
|
||||||
config.getStreamingMetadata().getPropertyFiles()[0].getFilename());
|
config.getStreamingMetadata().getPropertyFiles()[0].getFilename());
|
||||||
assertEquals(195, config.getStreamingMetadata().getPropertyFiles()[0].getOffset());
|
assertEquals(195, config.getStreamingMetadata().getPropertyFiles()[0].getOffset());
|
||||||
assertEquals(8, config.getStreamingMetadata().getPropertyFiles()[0].getSize());
|
assertEquals(8, config.getStreamingMetadata().getPropertyFiles()[0].getSize());
|
||||||
|
assertTrue(config.getAbConfig().getForceSwitchSlot());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -94,7 +97,8 @@ public class UpdateConfigTest {
|
||||||
@Test
|
@Test
|
||||||
public void getUpdatePackageFile_throwsErrorIfNotAFile() throws Exception {
|
public void getUpdatePackageFile_throwsErrorIfNotAFile() throws Exception {
|
||||||
String json = "{\"name\": \"upd\", \"url\": \"http://foo.bar\","
|
String json = "{\"name\": \"upd\", \"url\": \"http://foo.bar\","
|
||||||
+ " \"ab_install_type\": \"NON_STREAMING\"}";
|
+ " \"ab_install_type\": \"NON_STREAMING\","
|
||||||
|
+ " \"ab_config\": { \"force_switch_slot\": false } }";
|
||||||
UpdateConfig config = UpdateConfig.fromJson(json);
|
UpdateConfig config = UpdateConfig.fromJson(json);
|
||||||
thrown.expect(RuntimeException.class);
|
thrown.expect(RuntimeException.class);
|
||||||
config.getUpdatePackageFile();
|
config.getUpdatePackageFile();
|
||||||
|
|
|
@ -46,10 +46,11 @@ class GenUpdateConfig(object):
|
||||||
AB_INSTALL_TYPE_STREAMING = 'STREAMING'
|
AB_INSTALL_TYPE_STREAMING = 'STREAMING'
|
||||||
AB_INSTALL_TYPE_NON_STREAMING = 'NON_STREAMING'
|
AB_INSTALL_TYPE_NON_STREAMING = 'NON_STREAMING'
|
||||||
|
|
||||||
def __init__(self, package, url, ab_install_type):
|
def __init__(self, package, url, ab_install_type, ab_force_switch_slot):
|
||||||
self.package = package
|
self.package = package
|
||||||
self.url = url
|
self.url = url
|
||||||
self.ab_install_type = ab_install_type
|
self.ab_install_type = ab_install_type
|
||||||
|
self.ab_force_switch_slot = ab_force_switch_slot
|
||||||
self.streaming_required = (
|
self.streaming_required = (
|
||||||
# payload.bin and payload_properties.txt must exist.
|
# payload.bin and payload_properties.txt must exist.
|
||||||
'payload.bin',
|
'payload.bin',
|
||||||
|
@ -80,6 +81,9 @@ class GenUpdateConfig(object):
|
||||||
'url': self.url,
|
'url': self.url,
|
||||||
'ab_streaming_metadata': streaming_metadata,
|
'ab_streaming_metadata': streaming_metadata,
|
||||||
'ab_install_type': self.ab_install_type,
|
'ab_install_type': self.ab_install_type,
|
||||||
|
'ab_config': {
|
||||||
|
'force_switch_slot': self.ab_force_switch_slot,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def _gen_ab_streaming_metadata(self):
|
def _gen_ab_streaming_metadata(self):
|
||||||
|
@ -126,6 +130,11 @@ def main(): # pylint: disable=missing-docstring
|
||||||
default=GenUpdateConfig.AB_INSTALL_TYPE_NON_STREAMING,
|
default=GenUpdateConfig.AB_INSTALL_TYPE_NON_STREAMING,
|
||||||
choices=ab_install_type_choices,
|
choices=ab_install_type_choices,
|
||||||
help='A/B update installation type')
|
help='A/B update installation type')
|
||||||
|
parser.add_argument('--ab_force_switch_slot',
|
||||||
|
type=bool,
|
||||||
|
default=False,
|
||||||
|
help='if set true device will boot to a new slot, otherwise user manually '
|
||||||
|
'switches slot on the screen')
|
||||||
parser.add_argument('package',
|
parser.add_argument('package',
|
||||||
type=str,
|
type=str,
|
||||||
help='OTA package zip file')
|
help='OTA package zip file')
|
||||||
|
@ -144,7 +153,8 @@ def main(): # pylint: disable=missing-docstring
|
||||||
gen = GenUpdateConfig(
|
gen = GenUpdateConfig(
|
||||||
package=args.package,
|
package=args.package,
|
||||||
url=args.url,
|
url=args.url,
|
||||||
ab_install_type=args.ab_install_type)
|
ab_install_type=args.ab_install_type,
|
||||||
|
ab_force_switch_slot=args.ab_force_switch_slot)
|
||||||
gen.run()
|
gen.run()
|
||||||
gen.write(args.out)
|
gen.write(args.out)
|
||||||
print('Config is written to ' + args.out)
|
print('Config is written to ' + args.out)
|
||||||
|
|
Loading…
Reference in a new issue