!192 删除MainAbility

Merge pull request !192 from 王力量/deleteMain_weekly
This commit is contained in:
openharmony_ci 2024-06-26 04:02:04 +00:00 committed by Gitee
commit bc68610efe
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
12 changed files with 16 additions and 462 deletions

View File

@ -1,393 +0,0 @@
/*
* Copyright (c) 2023 Huawei Device Co., Ltd.
* 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 UIAbility from '@ohos.app.ability.UIAbility';
import dlpPermission from '@ohos.dlpPermission';
import emitter from '@ohos.events.emitter';
import Want from '@ohos.app.ability.Want';
import osAccount from '@ohos.account.osAccount';
import { Configuration } from '@ohos.app.ability.Configuration';
import Constants from '../common/constant';
import {
getAuthPerm,
checkDomainAccountInfo,
getOsAccountInfo,
judgeIsSandBox,
getFileFd,
getAppId,
getDLPInfo,
sendDlpManagerAccountLogin,
DLPInfo,
showErrorDialogAndExit,
isValidPath
} from '../common/utils';
import GlobalContext from '../common/GlobalContext';
import HomeFeature from '../feature/HomeFeature';
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import { BusinessError } from '@ohos.base';
import window from '@ohos.window';
import common from '@ohos.app.ability.common';
import { AccountTipsConfig } from '../common/AccountTipsConfig';
import { GetAlertMessage } from '../common/GetAlertMessage';
import { HiLog } from '../common/HiLog';
import FileUtils from '../common/FileUtils';
const TAG = 'Main';
let direction: number = -1;
export default class MainAbility extends UIAbility {
private authPerm: dlpPermission.DLPFileAccess = dlpPermission.DLPFileAccess.READ_ONLY;
private callerToken:number = 0;
private homeFeature!: HomeFeature;
async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> {
GlobalContext.store('abilityWant', want);
if (!GlobalContext.load('fileOpenHistoryFromMain')) {
GlobalContext.store('fileOpenHistoryFromMain', new Map<string, string>());
}
GlobalContext.store('uri', want.uri ?? '');
direction = this.context.config.direction ?? -1;
if (!GlobalContext.load('linkSet')) {
GlobalContext.store('linkSet', new Set<string>());
}
this.homeFeature = new HomeFeature(this.context);
GlobalContext.store('homeFeature', this.homeFeature);
}
onConfigurationUpdate(newConfig: Configuration): void {
if (direction !== newConfig.direction) {
direction = newConfig.direction ?? -1;
}
let eventData: emitter.EventData = {
data: {
'direction': direction,
}};
let innerEvent: emitter.InnerEvent = {
eventId: Constants.ENCRYPTION_EMIT_DIRECTION_STATUS,
priority: emitter.EventPriority.HIGH
};
emitter.emit(innerEvent, eventData);
}
onDestroy(): void {
HiLog.info(TAG, `onDestroy`);
if (GlobalContext.load('fileOpenHistoryFromMain')) {
(GlobalContext.load('fileOpenHistoryFromMain') as Map<string, Object>).delete(GlobalContext.load('uri') as string)
}
}
onNewWant(want: Want) {
HiLog.info(TAG, `onNewWant start`);
this.getNewWantPage(want);
}
async gotoPage(windowStage: window.WindowStage): Promise<void> {
let accountInfo: osAccount.OsAccountInfo = GlobalContext.load('accountInfo');
let accountName: string = accountInfo.domainInfo.accountName;
this.authPerm = getAuthPerm(accountName, GlobalContext.load('dlpProperty'));
AppStorage.setOrCreate('authPerm', this.authPerm);
AppStorage.setOrCreate<string>('contactAccount', GlobalContext.load('dlpProperty').contactAccount);
AppStorage.setOrCreate('validity', GlobalContext.load('dlpProperty').expireTime)
if (this.authPerm < dlpPermission.DLPFileAccess.READ_ONLY ||
this.authPerm > dlpPermission.DLPFileAccess.FULL_CONTROL) {
await showErrorDialogAndExit(windowStage, { code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError);
return;
}
if (this.authPerm === dlpPermission.DLPFileAccess.FULL_CONTROL) {
try {
await this.checkValidEnterpriseAndAccount(windowStage);
} catch {
return;
}
windowStage.loadContent('pages/changeEncryption', (err: BusinessError) =>{
if (err && err.code !== 0) {
HiLog.error(TAG, `loadContent failed: ${JSON.stringify(err)}`);
}
});
} else {
windowStage.loadContent('pages/permissionStatus', (err: BusinessError) =>{
if (err && err.code !== 0) {
HiLog.error(TAG, `loadContent failed: ${JSON.stringify(err)}`);
}
});
}
windowStage.getMainWindow().then((win: window.Window) => {
win.setWindowBackgroundColor('#00FFFFFF');
});
}
checkValidWant(want: Want): boolean {
let parameters = want.parameters;
if (parameters === undefined) {
HiLog.error(TAG, `need parameters in want`);
return false;
}
if (parameters.fileName === undefined) {
HiLog.error(TAG, `need fileName in want.parameters`);
return false;
}
if ((parameters.fileName as Record<string, string>).name === undefined) {
HiLog.error(TAG, `need name in want.parameters.fileName`);
return false;
}
if (want.uri === undefined) {
HiLog.error(TAG, `need uri in want`);
return false;
}
this.callerToken = parameters['ohos.aafwk.param.callerToken'] as number;
let callerBundleName: string = parameters['ohos.aafwk.param.callerBundleName'] as string;
if (this.callerToken === undefined || callerBundleName === undefined) {
HiLog.error(TAG, `need caller info in want.parameters`);
return false;
}
AppStorage.setOrCreate('hiPkgName', callerBundleName);
let uri = String(want.uri);
if ((GlobalContext.load('linkSet') as Set<string>).has(uri)) {
HiLog.error(TAG, `invalid uri for opened link uri`);
return false;
}
if (uri.indexOf(Constants.FUSE_PATH) !== -1 || !isValidPath(uri)) {
HiLog.error(TAG, `invalid uri in want.uri`);
return false;
}
return true;
}
async checkValidEnterpriseAndAccount(windowStage: window.WindowStage): Promise<void> {
return new Promise(async (resolve, reject) => {
let accountInfo: osAccount.OsAccountInfo = GlobalContext.load('accountInfo');
AccountTipsConfig.getAccountInfo(accountInfo.domainInfo.accountName)
.then(() => {
resolve();
})
.catch(async (error: BusinessError) => {
await showErrorDialogAndExit(windowStage, error);
reject();
return;
})
})
}
async checkValidWantAndAccount(want: Want): Promise<void> {
return new Promise(async (resolve, reject) => {
if (!this.checkValidWant(want)) {
await GetAlertMessage.startAlertAbility(this.context,
{ code: Constants.ERR_JS_APP_PARAM_ERROR } as BusinessError);
reject();
return;
}
let accountInfo: osAccount.OsAccountInfo;
try {
accountInfo = await getOsAccountInfo();
GlobalContext.store('accountInfo', accountInfo);
AppStorage.setOrCreate('accountDomain', accountInfo.domainInfo.domain);
resolve();
} catch (err) {
HiLog.error(TAG, `getOsAccountInfo failed: ${JSON.stringify(err)}`);
await GetAlertMessage.startAlertAbility(this.context,
{ code: Constants.ERR_JS_GET_ACCOUNT_ERROR } as BusinessError);
reject();
return;
}
let codeMessage = checkDomainAccountInfo(accountInfo);
if (codeMessage) {
await GetAlertMessage.startAlertAbility(this.context,
{ code: codeMessage } as BusinessError);
reject();
return;
}
})
}
async checkIsDLPFeatureProvided(windowStage: window.WindowStage): Promise<void> {
return new Promise(async (resolve, reject) => {
await dlpPermission.isDLPFeatureProvided().then((res: Boolean) => {
HiLog.info(TAG, `isDLPFeatureProvided: ${res}`);
if (res === false) {
showErrorDialogAndExit(windowStage, { code: Constants.ERR_JS_DLP_NOT_PROVIDED } as BusinessError);
reject();
return;
}
resolve();
}).catch((err: BusinessError) => {
HiLog.error(TAG, `isDLPFeatureProvided error: ${JSON.stringify(err)}`);
showErrorDialogAndExit(windowStage, { code: Constants.ERR_JS_DLP_NOT_PROVIDED } as BusinessError);
reject();
return;
});
})
}
async getNewWantPage(want: Want): Promise<void> {
HiLog.info(TAG, `getNewWantPage start`);
let windowStage: window.WindowStage = GlobalContext.load('windowStage') as window.WindowStage;
try {
await this.checkIsDLPFeatureProvided(windowStage);
} catch {
return;
}
try {
await this.checkValidWantAndAccount(want)
} catch {
return;
}
GlobalContext.store('abilityWant', want);
GlobalContext.store('uri', (GlobalContext.load('abilityWant') as Want).uri as string);
sendDlpManagerAccountLogin(0);
let requestIsFromSandBox: boolean = await judgeIsSandBox(want);
GlobalContext.store('requestIsFromSandBox', requestIsFromSandBox);
HiLog.info(TAG, `request is from sandbox: ${requestIsFromSandBox}`);
if (requestIsFromSandBox) {
this.requestIsFromSandBox(windowStage);
} else {
this.requestIsNotFromSandBox(windowStage);
}
}
requestIsFromSandBox(windowStage: window.WindowStage): void {
let abilityWant: Want = GlobalContext.load('abilityWant') as Want;
const linkFileName: string = (abilityWant.parameters?.linkFileName as Record<string, string>)?.name;
this.homeFeature.sandBoxLinkFileHome(linkFileName, this.callerToken,
(err: number, data: dlpPermission.DLPProperty, uri: string) => {
if (err !== 0) {
return;
}
let dlpFileName: string = (abilityWant.parameters?.fileName as Record<string, string>)?.name;
GlobalContext.store('dlpFileName', dlpFileName);
GlobalContext.store('linkFileName', linkFileName);
GlobalContext.store('dlpProperty', data);
AppStorage.setOrCreate('permanent', data.expireTime === 0);
if (data.expireTime !== 0) {
AppStorage.setOrCreate('validity', new Date(data.expireTime as number));
}
GlobalContext.store('uri', uri ?? '');
this.gotoPage(windowStage);
});
}
async requestIsNotFromSandBox(windowStage: window.WindowStage): Promise<void> {
let fileName: string =
((GlobalContext.load('abilityWant') as Want).parameters?.fileName as Record<string, string>)?.name;
let isDlpSuffix: boolean = false;
try {
isDlpSuffix = await FileUtils.isDLPFile(GlobalContext.load('uri'));
} catch {
await showErrorDialogAndExit(windowStage, { code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError);
return;
}
if (!isDlpSuffix) {
HiLog.info(TAG, `${fileName} is not a dlp file`);
GlobalContext.store('originFileName', fileName);
GlobalContext.store('originFd', getFileFd(GlobalContext.load('uri') as string));
windowStage.loadContent('pages/encryptionProtection', (err: BusinessError) =>{
if (err && err.code !== 0) {
HiLog.error(TAG, `loadContent failed: ${JSON.stringify(err)}`);
}
});
windowStage.getMainWindow().then((win: window.Window) => {
win.setWindowBackgroundColor('#00FFFFFF');
});
return;
} else {
this.dlpFilesToEncrypt(windowStage);
}
}
async dlpFilesToEncrypt(windowStage: window.WindowStage): Promise<void> {
let uri: string = GlobalContext.load('uri') as string;
try {
await this.findFileOpenHistoryHome(windowStage, uri);
} catch {
return;
}
let dlpFileName: string =
((GlobalContext.load('abilityWant') as Want).parameters?.fileName as Record<string, string>)?.name;
GlobalContext.store('dlpFileName', dlpFileName);
let callerAppId: string;
try {
let callerBundleName = Constants.DLP_MANAGER_BUNDLE_NAME;
callerAppId = await getAppId(callerBundleName);
HiLog.info(TAG, `get AppId: ${callerAppId}`);
} catch {
HiLog.info(TAG, `get AppId failed`);
return;
}
this.homeFeature.openDlpFileHome(uri, callerAppId,
async (err: number, data: dlpPermission.DLPProperty, msg: string) => {
if (err !== 0) {
let ansErr: BusinessError<void> = {
code: err,
name: '',
message: msg,
}
await showErrorDialogAndExit(windowStage, ansErr);
return;
} else {
GlobalContext.store('dlpProperty', data);
AppStorage.setOrCreate('permanent', data.expireTime === 0);
GlobalContext.store('fileOpenHistoryFromMain', new Map<string, string>());
if (data.expireTime !== 0) {
AppStorage.setOrCreate('validity', new Date(data.expireTime as number));
}
let fileOpenHistoryFromMain: Map<string, string> =
GlobalContext.load('fileOpenHistoryFromMain') as Map<string, string>;
fileOpenHistoryFromMain.set(uri, uri);
HiLog.info(TAG, `fileOpenHistoryFromMain add: ${JSON.stringify(fileOpenHistoryFromMain)}`);
this.gotoPage(windowStage);
}
})
}
findFileOpenHistoryHome(windowStage: window.WindowStage, uri: string): Promise<void> {
return new Promise<void>((resolve, reject) => {
this.homeFeature.fileOpenHistoryHome(uri, async (err: number) => {
if (err === 0) {
await showErrorDialogAndExit(windowStage, { code: Constants.ERR_JS_APP_OPEN_REJECTED } as BusinessError);
reject();
}
resolve();
})
})
}
async onWindowStageCreate(windowStage: window.WindowStage): Promise<void> {
HiLog.info(TAG, `onWindowStageCreate`);
GlobalContext.store('windowStage', windowStage);
let dlpInfo:DLPInfo = await getDLPInfo();
AppStorage.setOrCreate('hiPNameId', dlpInfo.name);
AppStorage.setOrCreate('hiPVersionId', dlpInfo.versionCode);
this.homeFeature.connectServiceExtAbility(()=>{
HiLog.info(TAG, `onWindowStageCreate callback`);
this.getNewWantPage(GlobalContext.load('abilityWant') as Want);
});
HiLog.info(TAG, `onWindowStageCreate end`);
}
onWindowStageDestroy(): void {
HiLog.info(TAG, `onWindowStageDestroy`);
}
onForeground(): void {
HiLog.info(TAG, `onForeground`);
}
onBackground() {
HiLog.info(TAG, `onBackground`);
}
};

View File

@ -316,7 +316,6 @@ export default class MainAbility extends UIExtensionAbility {
if (accountType === dlpPermission.AccountType.DOMAIN_ACCOUNT) {
this.dlpFilesToEncrypt(session, want);
} else {
GlobalContext.store('accountType', Constants.ADD_ACCOUNT_TYPE);
await GetAlertMessage.startAlertAbility(this.context, {
code: Constants.ERR_JS_APP_CANNOT_OPEN } as BusinessError);
}

View File

@ -132,7 +132,6 @@ export class GetAlertMessage {
}
if ([
Constants.ERR_JS_DLP_FILE_READ_ONLY,
Constants.ERR_JS_OFFLINE,
Constants.ERR_JS_SYSTEM_NEED_TO_BE_UPGRADED,
].includes(error.code)) {
windowHeight = Constants.START_ABILITY_HEIGHT_FOUR_ROWS;
@ -175,6 +174,8 @@ export class GetAlertMessage {
return { 'msg': $r('app.string.Function_Is_Not_Available') } as Record<string, Resource>;
case Constants.ERR_JS_OTHER_APP_OPEN_FILE:
return { 'msg': $r('app.string.This_File_is_Open_By_Other_App') } as Record<string, Resource>;
case Constants.ERR_JS_OFFLINE:
return { 'msg': $r('app.string.network_invalid') } as Record<string, Resource>;
default:
if (defaultTitle !== undefined && defaultMessage != undefined) {
return { 'title': defaultTitle, 'msg': defaultMessage } as Record<string, Resource>;
@ -243,13 +244,6 @@ export class GetAlertMessage {
'cancel': $r('app.string.ban'),
'ok': $r('app.string.SYSTEM_IS_AUTHENTICATED_LOGIN')
} as Record<string, Resource>;
case Constants.ERR_JS_OFFLINE:
return {
'title': $r('app.string.Network_not_connected'),
'msg': $r('app.string.Network_not_connected_description'),
'cancel': $r('app.string.ban'),
'ok': $r('app.string.Go_setting')
} as Record<string, Resource>;
default:
return {
'title': $r('app.string.header_title'),

View File

@ -251,5 +251,4 @@ export default class Constants {
public static readonly ERR_CODE_CONNECTION_FAIL: number = -301;
public static readonly ERR_CODE_CONNECTION_TIME_OUT: number = -310;
public static readonly ERR_CODE_SUCCESS: number = 0;
public static readonly ADD_ACCOUNT_TYPE: number = 3;
}

View File

@ -103,7 +103,7 @@ function getFileFd(uri: string, mode?: number): number {
async function getOsAccountInfo(): Promise<account_osAccount.OsAccountInfo> {
let accountMgr = account_osAccount.getAccountManager();
return await accountMgr.getCurrentOsAccount();
return await accountMgr.queryOsAccount();
}
function checkDomainAccountInfo(accountInfo: account_osAccount.OsAccountInfo): number {
@ -218,7 +218,6 @@ let toggleShow = (allNames: AuthAccount[], showNamesArr: AuthAccount[], editFlag
}
}
function directionStatus(func: Callback<number>) {
let innerEvent: emitter.InnerEvent = {
eventId: Constants.ENCRYPTION_EMIT_DIRECTION_STATUS
@ -228,18 +227,6 @@ function directionStatus(func: Callback<number>) {
});
}
async function showErrorDialogAndExit(windowStage: window.WindowStage, error: BusinessError): Promise<void> {
let abilityWant = GlobalContext.load('abilityWant') as Want;
if (abilityWant.parameters) {
abilityWant.parameters.error = error;
}
windowStage.loadContent('pages/alert', (err: BusinessError) => {
if (err.code !== 0) {
HiLog.error(TAG, `loadContent failed: ${JSON.stringify(err)}`);
}
});
}
function getAppId(bundleName: string) {
return new Promise<string>(async resolve => {
let bundleFlags: number = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_SIGNATURE_INFO;
@ -525,7 +512,6 @@ export {
calculate,
toggleShow,
directionStatus,
showErrorDialogAndExit,
getAppId,
getTime,
getFileSizeByUri,

View File

@ -88,11 +88,6 @@ struct Index {
].includes(messageCode)) {
this.messageHeight = Constants.START_ABILITY_MESSAGE_HEIGHT;
}
if ([
Constants.ERR_JS_OFFLINE,
].includes(messageCode)) {
this.messageHeight = Constants.START_ABILITY_MESSAGE_HEIGHT1;
}
if ([
Constants.ERR_JS_APP_PARAM_ERROR,
Constants.ERR_JS_APP_OPEN_REJECTED,
@ -130,7 +125,7 @@ struct Index {
Constants.ERR_JS_SYSTEM_NEED_TO_BE_UPGRADED,
].includes(messageCode)) {
errInfo = GetAlertMessage.getAlertTitleMessage(errorMsg);
} else if ([Constants.ERR_JS_APP_SYSTEM_IS_AUTHENTICATED, Constants.ERR_JS_OFFLINE].includes(messageCode)) {
} else if ([Constants.ERR_JS_APP_SYSTEM_IS_AUTHENTICATED].includes(messageCode)) {
errInfo = GetAlertMessage.getAlertButtonMessage(errorMsg);
} else if ([Constants.ERR_JS_USER_NO_PERMISSION].includes(messageCode)) {
errInfo = accountType === dlpPermission.AccountType.CLOUD_ACCOUNT ?
@ -208,16 +203,6 @@ struct Index {
.height(Constants.FOOTER_BUTTON_HEIGHT)
.onClick(async (event) => {
let error = this.alertWant?.parameters?.error as BusinessError;
if (error && error.code === Constants.ERR_JS_OFFLINE) {
let want: Want = {
action: 'action.system.home',
entities: ['entity.system.home'],
uri: 'wifi_entry'
};
(getContext(this) as common.UIAbilityContext).startAbility(want);
(getContext(this) as common.UIAbilityContext).terminateSelf();
return;
}
this.authWithPop();
})
.margin({ left: Constants.ENCRYPTION_PROTECTION_BUTTON_MARGIN })

View File

@ -890,6 +890,6 @@ struct changeEncryption {
}
}
}
.backgroundColor($r('app.color.base_background_color_dark'))
.backgroundColor($r('sys.color.mask_fourth'))
}
}

View File

@ -173,6 +173,7 @@ struct encryptedSharing {
AppStorage.setOrCreate('hiCode', 201);
sendDlpFileCreateProperties(dlpPermission.AccountType.CLOUD_ACCOUNT); // 201: DLP_2C_FILE_CREATE_EVENT
this.backToPages(filePathUri, filePatch);
HiLog.info(TAG, `beginToGenerateDLPFile success`);
} catch (err) {
this.enabledFocus = true;
HiLog.error(TAG, `open temp failed: ${JSON.stringify(err)}`);
@ -229,7 +230,11 @@ struct encryptedSharing {
AppStorage.setOrCreate('isInputInvalid', '');
return false;
}
if (!credentialCallBack.status && Number(credentialCallBack.errorCode) !== Constants.ERR_CODE_SUCCESS) {
if (!credentialCallBack.status && [
Constants.ERR_CODE_NETWORK_ERROR,
Constants.ERR_CODE_CONNECTION_FAIL,
Constants.ERR_CODE_CONNECTION_TIME_OUT
].includes(Number(credentialCallBack.errorCode))) {
promptAction.showToast({
message: $r('app.string.network_invalid'),
duration: Constants.SHARE_SET_TIMEOUT
@ -319,6 +324,7 @@ struct encryptedSharing {
.border(this.phoneFormatTips ?
{ width: Constants.DIALOG_MD_OFFSET, color: $r('sys.color.ohos_id_color_warning') } : { width: 0})
.onChange((value: string) => {
HiLog.info(TAG, `input length: ${value.length}`);
this.inputValue = value;
this.phoneFormatTips = false;
})

View File

@ -949,6 +949,6 @@ struct encryptionProtection {
}
}
}
.backgroundColor($r('app.color.base_background_color_dark'))
.backgroundColor($r('sys.color.mask_fourth'))
}
}

View File

@ -642,6 +642,6 @@ struct encryptionSuccess {
}
}
}
.backgroundColor($r('app.color.base_background_color_dark'))
.backgroundColor($r('sys.color.mask_fourth'))
}
}

View File

@ -19,18 +19,6 @@
}
],
"abilities": [
{
"name": "MainAbility",
"srcEntry": "./ets/MainAbility/MainAbility.ets",
"description": "$string:MainAbility_desc",
"icon": "$media:app_icon",
"label": "$string:MainAbility_label",
"startWindowIcon": "$media:app_icon",
"startWindowBackground": "$color:white",
"orientation": "auto_rotation_restricted",
"exported": true,
"removeMissionAfterTerminate": true
},
{
"name": "AlertAbility",
"srcEntry": "./ets/AlertAbility/AlertAbility.ets",
@ -40,6 +28,7 @@
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:white",
"excludeFromMissions": true,
"exported": true,
"removeMissionAfterTerminate": true
},
{

View File

@ -32,18 +32,6 @@
}
],
"abilities": [
{
"name": "MainAbility",
"srcEntry": "./ets/MainAbility/MainAbility.ets",
"description": "$string:MainAbility_desc",
"icon": "$media:app_icon",
"label": "$string:MainAbility_label",
"startWindowIcon": "$media:app_icon",
"startWindowBackground": "$color:white",
"orientation": "auto_rotation_restricted",
"exported": true,
"removeMissionAfterTerminate": true
},
{
"name": "AlertAbility",
"srcEntry": "./ets/AlertAbility/AlertAbility.ets",
@ -53,6 +41,7 @@
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:white",
"excludeFromMissions": true,
"exported": true,
"removeMissionAfterTerminate": true
},
{