@@ -117,6 +117,15 @@ arc.bg.getAppId = () => {
117117 } ) ;
118118 } ) ;
119119} ;
120+ arc . bg . getAppAnonumousId = ( ) => {
121+ return new Promise ( ( resolve ) => {
122+ chrome . storage . local . get ( {
123+ 'appAnonymousId' : null
124+ } , function ( result ) {
125+ resolve ( result . appAnonymousId ) ;
126+ } ) ;
127+ } ) ;
128+ } ;
120129arc . bg . releaseChannel = ( ) => {
121130 var manifest = chrome . runtime . getManifest ( ) ;
122131 // jscs:disable
@@ -149,22 +158,81 @@ arc.bg.notifyBetaUpdate = () => {
149158 }
150159 } ) ;
151160} ;
161+ /**
162+ * To ensure anynomous app usage the app is using browser's build in crypto functions.
163+ * It requires to generate some initial random data to encode a string.
164+ *
165+ * Generated values are not stored so they can't be used again. Clearing hashed app id
166+ * (anonumousId) can't be restored.
167+ */
168+ arc . bg . getAninimizedId = ( ) => {
169+ return arc . bg . getAppAnonumousId ( ) . then ( ( anonymousId ) => {
170+ if ( anonymousId ) {
171+ return anonymousId ;
172+ }
173+ // The app id is kept locally and it's available to the app.
174+ // It was used to identify user data on server storage.
175+ // It's not used right now since there's no app's server data synchroznization
176+ // but it's a subject to change.
177+ let encodedDataBuffer ;
178+ return arc . bg . getAppId ( )
179+ . then ( ( appId ) => {
180+ let encoder = new TextEncoder ( 'utf-8' ) ;
181+ encodedDataBuffer = encoder . encode ( appId ) ;
182+ let aesAlgorithmKeyGen = {
183+ name : 'AES-CBC' ,
184+ length : 128
185+ } ;
186+ return window . crypto . subtle . generateKey ( aesAlgorithmKeyGen , false , [ 'encrypt' ] ) ;
187+ } )
188+ . then ( ( cryptoKey ) => {
189+ let aesAlgorithmEncrypt = {
190+ name : 'AES-CBC' ,
191+ iv : window . crypto . getRandomValues ( new Uint8Array ( 16 ) )
192+ } ;
193+ return window . crypto . subtle . encrypt ( aesAlgorithmEncrypt , cryptoKey , encodedDataBuffer ) ;
194+ } )
195+ . then ( ( arrayBuffer ) => {
196+ let view = new Uint8Array ( arrayBuffer ) ;
197+ let arr = Array . prototype . slice . call ( view ) ;
198+ arr = arr . map ( function ( item ) {
199+ return String . fromCharCode ( item ) ;
200+ } ) ;
201+ return window . btoa ( arr . join ( '' ) ) ;
202+ } )
203+ . then ( ( base64 ) => {
204+ return new Promise ( ( resolve ) => {
205+ chrome . storage . local . set ( {
206+ appAnonymousId : base64
207+ } , function ( ) {
208+ resolve ( base64 ) ;
209+ } ) ;
210+ } ) ;
211+ } ) ;
212+ } ) ;
213+ } ;
152214/**
153215 * Very base replacement for GA session counter.
154216 * According to T&C of GA users must have ability to disable GA in the app.
155- * However usage stats are crutial for the app.
217+ * However usage (only usage which is # of app openings) stats are crutial for the app.
156218 * This function will send generated an nonymous ID to the app's backed to record the session.
157219 * This is the only information send to the backed (time and generated ID)
158220 */
159221arc . bg . recordSession = ( ) => {
160- arc . bg . getAppId ( ) . then ( ( appId ) => {
222+ arc . bg . getAninimizedId ( ) . then ( ( aid ) => {
161223 let d = new Date ( ) ;
162- let url = 'http://api.chromerestclient.com/analytics/record?ai=' ;
163- url += appId + '&t=' ;
164- url += d . getTime ( ) + '&tz=' ;
165- url += d . getTimezoneOffset ( ) ;
224+ // let url = 'http://localhost:8080/analytics/record';
225+ let url = 'http://api.chromerestclient.com/analytics/record' ;
226+ let data = new FormData ( ) ;
227+ data . append ( 'aid' , aid ) ; // anonymousId
228+ data . append ( 't' , d . getTime ( ) ) ; // time
229+ data . append ( 'tz' , d . getTimezoneOffset ( ) ) ; //timezone
230+ var headers = new Headers ( ) ;
231+ headers . append ( 'x-api-version' , '2' ) ;
166232 fetch ( url , {
167- method : 'POST'
233+ method : 'POST' ,
234+ body : data ,
235+ headers : headers
168236 } ) . catch ( ( ) => { } ) ;
169237 } ) ;
170238} ;
0 commit comments