1/**
2 * Copyright (c) 2021-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import common from '@ohos.app.ability.common';
17import Audio from '@ohos.multimedia.audio';
18import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
19import DataShareExtensionAbility from '@ohos.application.DataShareExtensionAbility';
20import rpc from '@ohos.rpc';
21import process from '@ohos.process';
22import settings from '@ohos.settings';
23import relationalStore from '@ohos.data.relationalStore';
24import Want from '@ohos.app.ability.Want';
25import dataSharePredicates from '@ohos.data.dataSharePredicates';
26import { AsyncCallback, BusinessError } from '@ohos.base';
27import SettingsDataConfig from '../Utils/SettingsDataConfig';
28import SettingsDBHelper from '../Utils/SettingsDBHelper';
29import { Log } from '../Utils/Log';
30import { GlobalContext}  from '../Utils/GlobalContext';
31import contextConstant from '@ohos.app.ability.contextConstant';
32
33
34interface  IRequest {
35  operation:string ;
36  columns:string[];
37  predicates:dataSharePredicates.DataSharePredicates|relationalStore.RdbPredicates|null;
38  value:relationalStore.ValuesBucket|null
39}
40let rdbStore:relationalStore.RdbStore|undefined = undefined;
41let requests:IRequest[] = [];
42let SETTINGS_AUDIO_RINGTONE = 'settings.audio.ringtone'
43let SETTINGS_AUDIO_MEDIA = 'settings.audio.media'
44let SETTINGS_AUDIO_VOICE_CALL = 'settings.audio.voicecall'
45let trustList: String[] = [
46settings.display.SCREEN_BRIGHTNESS_STATUS,
47settings.display.AUTO_SCREEN_BRIGHTNESS,
48settings.display.SCREEN_OFF_TIMEOUT
49];
50let ret:number = 0;
51let err:BusinessError = {'code':-1} as BusinessError;
52
53export default class DataExtAbility extends DataShareExtensionAbility {
54  onCreate(want: Want) {
55    GlobalContext.getContext().setObject('abilityContext', this.context);
56    // database move to DE area;
57    this.context.area = contextConstant.AreaMode.EL2;
58    this.context.area = SettingsDBHelper.getInstance().getArea();
59    this.onInitialized();
60    Log.info('onCreate  context' + JSON.stringify(this.context));
61  }
62
63  onInitialized() {
64    Log.info('onInitialize start');
65    let context = GlobalContext.getContext().getObject('abilityContext') as Context;
66    Log.info('onInitialize start context: ' + JSON.stringify(this.context));
67    if (context !== null) {
68      SettingsDBHelper.getInstance().getRdbStore().then((rdb: relationalStore.RdbStore|undefined) => {
69        rdbStore = rdb;
70        if (!rdbStore) {
71          Log.error('onInitialized: get rdb store failed!');
72          return;
73        }
74        Log.info('onInitialized: get rdb store succeed!');
75        for (let i = 0; i < requests.length; i++) {
76          let opt: string = requests[i].operation;
77          let columns: string[] = requests[i].columns;
78          let predicates = (requests[i].predicates) as dataSharePredicates.DataSharePredicates;
79
80          let value: relationalStore.ValuesBucket|null = requests[i].value;
81          if (opt == 'insert') {
82            if(value){
83              rdbStore?.insert(SettingsDataConfig.TABLE_NAME, value,  (err, ret) => {
84                Log.info('onInitialized insert ret: ' + ret);
85              });
86            }
87          } else if (opt == 'query') {
88            if(predicates){
89              rdbStore?.query(SettingsDataConfig.TABLE_NAME, predicates, columns, (
90                err: BusinessError, resultSet: relationalStore.ResultSet) => {
91                Log.info('onInitialized query ret: ' + JSON.stringify(resultSet));
92              });
93            }
94
95          } else if (opt == 'update') {
96            if(value){
97              rdbStore?.update(SettingsDataConfig.TABLE_NAME, value, predicates, (err, ret) => {
98                Log.info('onInitialized update ret: ' + ret);
99              });
100            }
101          }
102        }
103      }).catch((err: Error) => {
104        Log.error('onInitialize failed:' + JSON.stringify(err));
105      })
106    } else {
107      Log.info('onInitialize context error!');
108    }
109    Log.info('onInitialize end');
110  }
111
112  insert(uri: string, value: relationalStore.ValuesBucket, callback : AsyncCallback<number>) {
113    Log.info('insert keyword = ' + value[SettingsDataConfig.FIELD_KEYWORD] + ' start:' + uri);
114    let rdbInsert = (GrantStatus: boolean) => {
115      if (!GrantStatus) {
116        callback(err, ret);
117        return;
118      }
119      this.DoSystemSetting(
120        value[SettingsDataConfig.FIELD_KEYWORD]?.toString(), value[SettingsDataConfig.FIELD_VALUE]?.toString());
121
122      if (rdbStore == null) {
123        let request: IRequest = {
124          operation: 'insert', columns: [], predicates: null, value: value
125        };
126        Log.info('insert request = ' + JSON.stringify(request));
127        requests.push(request);
128        callback(err, ret);
129      } else {
130        rdbStore.insert(SettingsDataConfig.TABLE_NAME, value,  (err, ret) => {
131          Log.info('insert result: ' + ret);
132          callback(err, ret);
133        });
134      }
135    }
136
137    try {
138      Log.info('Start to verify permissions.');
139      this.verifyPermission(value, rdbInsert);
140    } catch (err) {
141      Log.error('Insert Data error:' + JSON.stringify(err));
142      callback(err, ret);
143    }
144  }
145
146
147  update(
148    uri: string, predicates: dataSharePredicates.DataSharePredicates,
149    value:relationalStore.ValuesBucket, callback: AsyncCallback<number>) {
150    Log.info('update keyword = ' + value[SettingsDataConfig.FIELD_KEYWORD] + ' start:' + uri);
151    let rdbUpData = (GrantStatus:boolean) => {
152      if (!GrantStatus) {
153        callback(err, ret);
154        return;
155      }
156      this.DoSystemSetting(
157        value[SettingsDataConfig.FIELD_KEYWORD]?.toString(), value[SettingsDataConfig.FIELD_VALUE]?.toString());
158      if (rdbStore == null) {
159        let request : IRequest = {
160          operation: 'update', columns: [], predicates: predicates, value: value
161        };
162        Log.info('update request = ' + JSON.stringify(request));
163        requests.push(request);
164        callback(err, ret);
165      } else {
166        rdbStore.update(SettingsDataConfig.TABLE_NAME, value, predicates , (err, ret)=> {
167          Log.info('update result: ' + ret);
168          callback(err, ret);
169        });
170      }
171    }
172    try {
173      Log.info('Start to verify permissions.');
174      this.verifyPermission(value, rdbUpData);
175    } catch (err) {
176      Log.error('upData error:' + JSON.stringify(err));
177      callback(err, ret);
178    }
179  }
180
181  delete(uri: string, predicates: dataSharePredicates.DataSharePredicates, callback: AsyncCallback<number>) {
182    Log.info('nothing to do');
183  }
184
185  query(uri: string,
186        predicates: dataSharePredicates.DataSharePredicates, columns: string[], callback: AsyncCallback<Object>) {
187    Log.info( 'query start uri:' + uri);
188    if (rdbStore == null) {
189      let request: IRequest = {operation:'query', columns : columns, predicates : predicates, value:null};
190      Log.info('query request = ' + JSON.stringify(request));
191      requests.push(request);
192      callback(err, {'_napiwrapper':{}});
193    } else {
194      rdbStore.query(SettingsDataConfig.TABLE_NAME, predicates, columns,
195        (err:BusinessError, resultSet:relationalStore.ResultSet)=> {
196        Log.info(
197          'query result:' + JSON.stringify(resultSet.rowCount) + 'columnNames' + JSON.stringify(resultSet.columnNames));
198        callback(err, resultSet);
199      })
200    }
201  }
202
203  private DoSystemSetting(settingsKey: string|undefined, settingsValue: string|undefined) {
204    switch (settingsKey) {
205      case SETTINGS_AUDIO_RINGTONE:
206        try {
207          let volumeType = Audio.AudioVolumeType.RINGTONE;
208          Audio.getAudioManager().setVolume(volumeType, Number(settingsValue)).then(() => {
209            Log.info('settings Promise returned to indicate a successful RINGTONE setting.')
210          });
211        } catch (err) {
212          Log.info('settings RINGTONE failed error = ' + JSON.stringify(err));
213        }
214        break
215      case SETTINGS_AUDIO_MEDIA:
216        try {
217          let volumeType = Audio.AudioVolumeType.MEDIA;
218          Audio.getAudioManager().setVolume(volumeType, Number(settingsValue)).then(() => {
219            Log.info('settings Promise returned to indicate a successful MEDIA setting.')
220          });
221        } catch (err) {
222          Log.info('settings MEDIA failed error = ' + JSON.stringify(err));
223        }
224        break
225      case SETTINGS_AUDIO_VOICE_CALL:
226        try {
227          let volumeType = Audio.AudioVolumeType.VOICE_CALL;
228          Audio.getAudioManager().setVolume(volumeType, Number(settingsValue)).then(() => {
229            Log.info('settings Promise returned to indicate a successful VOICE_CALL setting.')
230          });
231        } catch (err) {
232          Log.info('settings VOICE_CALL failed error = ' + JSON.stringify(err));
233        }
234        break
235      default:
236        Log.info(settingsKey + ' key is not audio');
237        break
238    }
239  }
240
241  private verifyPermission(value: relationalStore.ValuesBucket, callBack: (GrantStatus: boolean) => void ) {
242    if (this.isTrustList(
243      value[SettingsDataConfig.FIELD_KEYWORD] as string) || process.uid == rpc.IPCSkeleton.getCallingUid()) {
244      callBack(true);
245      return;
246    }
247    try {
248      let tokenID = rpc.IPCSkeleton.getCallingTokenId();
249      Log.info('tokenID = ' + tokenID);
250      let grantStatus = abilityAccessCtrl.createAtManager().verifyAccessToken(
251        tokenID, 'ohos.permission.MANAGE_SECURE_SETTINGS');
252      grantStatus.then(data => {
253        if (data == abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
254          Log.info('MANAGE_SECURE_SETTINGS active');
255          callBack(true);
256        } else {
257          Log.warn('MANAGE_SECURE_SETTINGS grantStatus= ' + JSON.stringify(data));
258          callBack(false);
259        }
260      }).catch((err: BusinessError) => {
261        Log.error('tokenID = ' + tokenID + ' verifyAccessToken is failed: ' + JSON.stringify(err));
262        callBack(false);
263      })
264    } catch (err) {
265      Log.error('err = ' + JSON.stringify(err));
266      callBack(false);
267    }
268  }
269
270  private isTrustList(keyWord: string): boolean {
271    return trustList.includes(keyWord)
272  }
273}