1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17// don't strip logging from release builds
18#define LOG_NDEBUG 0
19
20#include <android_runtime/AndroidRuntime.h>
21#include "utils/Log.h"
22#include "DMServiceMain.h"
23#include "dmt.hpp"
24#include "DMTreeManager.h"
25static jobject g_sessionObj;
26int g_cancelSession;
27
28//#define LOGV printf
29//#define LOGD printf
30
31
32#define SET_RET_STATUS_BUF \
33        jResult = jenv->NewByteArray(5); char szResult[5]; memset(szResult, 0, 5); \
34        ::snprintf(szResult, 5, "%4d", ret_status); \
35        jenv->SetByteArrayRegion(jResult, 0, 5, (const jbyte*)szResult)
36
37static void Dump(const char* buf, int size, boolean isBinary)
38{
39  if (!isBinary) {
40    // just print the string
41    char* szBuf = new char[size + 1];
42
43    memcpy(szBuf, buf, size);
44    szBuf[size] = 0;
45
46    LOGE("The test script error text:\n\n%s\n\n", szBuf);
47  } else {
48    int nOffset = 0;
49
50    while (size > 0) {
51      int nLine = size > 16 ? 16 : size;
52
53      char s[250];
54      int pos = 0;
55
56      pos += ::snprintf(s + pos, (250 - pos), "%04x:", nOffset);
57
58      for (int i = 0; i < nLine; i++) {
59        pos += ::snprintf(s + pos, (250 - pos), " %02x", (unsigned int)((unsigned char) buf[i]) );
60      }
61      for (int i = nLine; i < 16; i++) {
62        pos += ::snprintf(s + pos, (250 - pos), "   ");
63      }
64
65      pos += ::snprintf(s + pos, (250 - pos), "  ");
66      for (int i = 0; i < nLine; i++) {
67        pos += ::snprintf(s + pos, (250 - pos), "%c", (buf[i] > 31 ? buf[i] : '.') );
68      }
69
70      LOGE("%s\n", s);
71      buf += nLine;
72      size -= nLine;
73      nOffset += nLine;
74    }
75  }
76}
77
78/**
79 * check the input string is a a valid UTF-8 string or not
80 * 1 -- valid, 0 -- invalid
81 */
82static jint isUtf8Valid(const char* bytes) {
83    while (*bytes != '\0') {
84      uint8_t utf8 = *(bytes++);
85      // Switch on the high four bits.
86      switch (utf8 >> 4) {
87      case 0x00:
88      case 0x01:
89      case 0x02:
90      case 0x03:
91      case 0x04:
92      case 0x05:
93      case 0x06:
94      case 0x07:
95        // Bit pattern 0xxx. No need for any extra bytes.
96        break;
97      case 0x08:
98      case 0x09:
99      case 0x0a:
100      case 0x0b:
101      case 0x0f:
102        /*
103         * Bit pattern 10xx or 1111, which are illegal start bytes.
104         * Note: 1111 is valid for normal UTF-8, but not the
105         * Modified UTF-8 used here.
106         */
107        return 0;
108      case 0x0e:
109        // Bit pattern 1110, so there are two additional bytes.
110        utf8 = *(bytes++);
111        if ((utf8 & 0xc0) != 0x80) {
112          return 0;
113        }
114        // Fall through to take care of the final byte.
115      case 0x0c:
116      case 0x0d:
117        // Bit pattern 110x, so there is one additional byte.
118        utf8 = *(bytes++);
119        if ((utf8 & 0xc0) != 0x80) {
120          return 0;
121        }
122        break;
123      }
124    }
125    return 1;
126}
127
128
129JNIEXPORT jint
130initialize(JNIEnv* /*env*/, jobject /*jobj*/)
131{
132  LOGD("native initialize");
133  if (!DmtTreeFactory::Initialize()) {
134    LOGE("Failed to initialize DM\n");
135    return static_cast<jint>(SYNCML_DM_FAIL);
136  }
137
138  return static_cast<jint>(SYNCML_DM_SUCCESS);
139}
140
141JNIEXPORT jint
142destroy(JNIEnv* /*env*/, jobject /*jobj*/)
143{
144  LOGD("Enter destroy");
145  if (DmtTreeFactory::Uninitialize() != SYNCML_DM_SUCCESS) {
146    LOGE("Failed to uninitialize DM\n");
147    return static_cast<jint>(SYNCML_DM_FAIL);
148  }
149
150  LOGD("Leave destroy");
151  return static_cast<jint>(SYNCML_DM_SUCCESS);
152}
153
154JNIEXPORT jint
155parsePkg0(JNIEnv* env, jclass, jbyteArray jPkg0, jobject jNotification)
156{
157    LOGD("Enter parsePkg0");
158    jclass notifClass = env->GetObjectClass(jNotification);
159
160    if (jPkg0 == NULL) {
161        return static_cast<jint>(SYNCML_DM_FAIL);
162    }
163
164    jbyte* pkg0Buf = env->GetByteArrayElements(jPkg0, NULL);
165    jsize pkg0Len = env->GetArrayLength(jPkg0);
166
167    DmtNotification notif;
168    DmtPrincipal p("localhost");
169
170    SYNCML_DM_RET_STATUS_T ret = DmtTreeFactory::ProcessNotification(p, (UINT8*)pkg0Buf, (INT32)pkg0Len, notif);
171
172    if(ret == SYNCML_DM_FAIL) {
173	return static_cast<jint>(SYNCML_DM_FAIL);
174    }
175
176    jmethodID jSetServerID = env->GetMethodID( notifClass, "setServerID", "(Ljava/lang/String;)V");
177
178    if(isUtf8Valid(notif.getServerID().c_str())) {
179        jstring jServerID = env->NewStringUTF(notif.getServerID().c_str());
180        env->CallVoidMethod(jNotification, jSetServerID, jServerID);
181    } else {
182        LOGE("Invalid Server ID, not legal UTF8");
183        return static_cast<jint>(SYNCML_DM_FAIL);
184    }
185
186
187    jmethodID jSetSessionID = env->GetMethodID( notifClass, "setSessionID", "(I)V");
188    env->CallVoidMethod(jNotification, jSetSessionID, (jint)notif.getSessionID());
189
190    jmethodID jSetUIMode = env->GetMethodID( notifClass, "setUIMode", "(I)V");
191    env->CallVoidMethod(jNotification, jSetUIMode, (jint)notif.getUIMode());
192
193    jmethodID jSetInitiator = env->GetMethodID( notifClass, "setInitiator", "(I)V");
194    env->CallVoidMethod(jNotification, jSetInitiator, (jint)notif.getInitiator());
195
196    jmethodID jSetAuthFlag = env->GetMethodID( notifClass, "setAuthFlag", "(I)V");
197    env->CallVoidMethod(jNotification, jSetAuthFlag, (jint)notif.getAuthFlag());
198
199    env->ReleaseByteArrayElements(jPkg0, pkg0Buf, 0);
200
201    LOGD("Leave parsePkg0, ret: %d", ret);
202    return static_cast<jint>(ret);
203}
204
205
206JNIEXPORT jint JNICALL startFotaClientSession(JNIEnv* jenv, jclass,
207        jstring jServerId, jstring jAlertStr, jobject jdmobj)
208{
209    LOGV("In native startFotaClientSession\n");
210
211    SYNCML_DM_RET_STATUS_T ret_status = SYNCML_DM_FAIL;
212    DMString serverID;
213
214    g_sessionObj = jdmobj;
215
216    const char* szDmServerId = jenv->GetStringUTFChars(jServerId, NULL);
217    const char* szDMAlertStr = NULL;
218    if (jAlertStr != NULL) {
219        szDMAlertStr = jenv->GetStringUTFChars(jAlertStr, NULL);
220    }
221
222    DmtPrincipal principal(szDmServerId);
223
224    DMString alertURI("./DevDetail/Ext/SystemUpdate");
225    DMString strEmpty;
226    DmtFirmAlert alert(alertURI, strEmpty, szDMAlertStr, "chr", strEmpty, strEmpty);
227    DmtSessionProp prop(alert, true);
228
229    g_cancelSession = 0;
230
231    ret_status = DmtTreeFactory::StartServerSession(principal, prop);
232
233    if (jAlertStr != NULL) {
234        jenv->ReleaseStringUTFChars(jAlertStr, szDMAlertStr);
235    }
236
237    g_sessionObj = NULL;
238    if (ret_status == SYNCML_DM_SUCCESS) {
239        LOGV("Native startFotaClientSession return successfully\n");
240        return static_cast<jint>(SYNCML_DM_SUCCESS);
241    } else {
242        LOGE("Native startFotaClientSession return error %d\n", ret_status);
243        return static_cast<jint>(ret_status);
244    }
245}
246
247JNIEXPORT jint JNICALL startClientSession(JNIEnv* jenv, jclass,
248        jstring jServerId, jobject jdmobj)
249{
250    LOGV("In native startClientSession\n");
251
252    SYNCML_DM_RET_STATUS_T ret_status = SYNCML_DM_FAIL;
253    DMString serverID;
254
255    g_sessionObj = jdmobj;
256    const char* szDmServerId = jenv->GetStringUTFChars(jServerId, NULL);
257    DmtPrincipal principal(szDmServerId);
258
259    DmtSessionProp prop(true);
260
261    g_cancelSession = 0;
262
263    ret_status = DmtTreeFactory::StartServerSession(principal, prop);
264
265    jenv->ReleaseStringUTFChars(jServerId, szDmServerId);
266
267    g_sessionObj = NULL;
268    if (ret_status == SYNCML_DM_SUCCESS) {
269        LOGV("Native startClientSession return successfully\n");
270    } else {
271        LOGV("Native startClientSession return error %d\n", ret_status);
272    }
273    return static_cast<jint>(ret_status);
274}
275
276JNIEXPORT jint JNICALL startFotaServerSession(JNIEnv* jenv, jclass,
277        jstring jServerId, jint sessionID, jobject jdmobj)
278{
279    LOGV("In native startFotaServerSession\n");
280
281    g_sessionObj = jdmobj;
282
283    const char* szDmServerId = jenv->GetStringUTFChars(jServerId, NULL);
284    DmtPrincipal principal(szDmServerId);
285    DmtSessionProp prop(static_cast<UINT16>(sessionID), true);
286
287    g_cancelSession = 0;
288
289    SYNCML_DM_RET_STATUS_T ret_status = DmtTreeFactory::StartServerSession(principal, prop);
290
291    jenv->ReleaseStringUTFChars(jServerId, szDmServerId);
292
293    g_sessionObj = NULL;
294
295    if (ret_status == SYNCML_DM_SUCCESS) {
296        LOGV("Native startFotaServerSession return successfully\n");
297    } else {
298        LOGV("Native startFotaServerSession return error %d\n", ret_status);
299    }
300    return static_cast<jint>(ret_status);
301}
302
303JNIEXPORT jint JNICALL startFotaNotifySession(JNIEnv* jenv, jclass,
304        jstring result, jstring pkgURI, jstring alertType,
305        jstring serverID, jstring correlator, jobject jdmobj)
306{
307    g_sessionObj = jdmobj;
308
309    const char* szResult = jenv->GetStringUTFChars(result, NULL);
310    const char* szPkgURI = jenv->GetStringUTFChars(pkgURI, NULL);
311    const char* szAlertType = jenv->GetStringUTFChars(alertType, NULL);
312    const char* szDmServerId = jenv->GetStringUTFChars(serverID, NULL);
313    const char* szCorrelator = jenv->GetStringUTFChars(correlator, NULL);
314
315    DmtPrincipal principal(szDmServerId);
316    DmtFirmAlert alert(szPkgURI, szResult, szAlertType, "chr", NULL, szCorrelator);
317    DmtSessionProp prop(alert, true);
318
319    SYNCML_DM_RET_STATUS_T dm_result = SYNCML_DM_SUCCESS;
320    g_cancelSession = 0;
321
322    dm_result = DmtTreeFactory::StartServerSession(principal, prop);
323
324    jenv->ReleaseStringUTFChars(result, szResult);
325    jenv->ReleaseStringUTFChars(pkgURI, szPkgURI);
326    jenv->ReleaseStringUTFChars(alertType, szAlertType);
327    jenv->ReleaseStringUTFChars(serverID, szDmServerId);
328    jenv->ReleaseStringUTFChars(correlator, szCorrelator);
329
330    g_sessionObj = NULL;
331    if (dm_result == SYNCML_DM_SUCCESS) {
332        LOGV("Native startFotaNotifySession return successfully\n");
333    } else {
334        LOGV("Native startFotaNotifySession return error %d\n", dm_result);
335    }
336    return static_cast<jint>(dm_result);
337}
338
339jobject getNetConnector()
340{
341    JNIEnv* env = android::AndroidRuntime::getJNIEnv();
342
343    jclass jdmSessionClz = env->GetObjectClass(g_sessionObj);
344    jmethodID jgetNet = env->GetMethodID(jdmSessionClz,
345            "getNetConnector",
346            "()Lcom/android/omadm/service/DMHttpConnector;");
347    return env->CallObjectMethod(g_sessionObj, jgetNet);
348}
349
350jobject getDMAlert(JNIEnv* env)
351{
352   LOGD(("DM Alert: enter getDMAlert()"));
353   if (NULL == g_sessionObj) {
354       LOGE(("DM Alert: g_sessionObj is NULL!"));
355       return NULL;
356   }
357
358   jclass jdmSessionClz = env->GetObjectClass(g_sessionObj);
359   if (NULL == jdmSessionClz) {
360       LOGE(("DM Alert: env->GetObjectClass(g_sessionObj) failed!"));
361       return NULL;
362   }
363   LOGD(("DM Alert: success env->GetObjectClass(...)"));
364
365   jmethodID jdmGetDMAlert = env->GetMethodID(jdmSessionClz,
366            "getDMAlert",
367            "()Lcom/android/omadm/service/DMAlert;");
368   if ( NULL == jdmGetDMAlert ) {
369       LOGE(("DM Alert: env->GetMethodID(jdmSessionClz) failed!"));
370       return NULL;
371   }
372   LOGD(("DM Alert: success env->GetMethodID(...)"));
373
374   return env->CallObjectMethod(g_sessionObj, jdmGetDMAlert);
375}
376
377JNIEXPORT jint JNICALL cancelSession(JNIEnv*, jclass)
378{
379    g_cancelSession = 1;
380    return static_cast<jint>(SYNCML_DM_SUCCESS);
381}
382
383JNIEXPORT jstring JNICALL parseBootstrapServerId(JNIEnv* jenv, jclass, jbyteArray jMsgBuf,
384        jboolean isWbxml)
385{
386    jint retCode = 0;
387    jstring jServerId = NULL;
388
389    SYNCML_DM_RET_STATUS_T dm_ret_status;
390
391    jbyte* jBuf = jenv->GetByteArrayElements(jMsgBuf, NULL);
392    jsize jBufSize = jenv->GetArrayLength(jMsgBuf);
393
394    DmtPrincipal principal("DM_BOOTSTRAP");
395    DMString strServerId;
396    dm_ret_status = DmtTreeFactory::Bootstrap(principal, (const UINT8*)jBuf, jBufSize, isWbxml,
397            false, strServerId);
398
399    LOGD("parseBootstrapServerId dm_ret_status: %d", dm_ret_status);
400
401    if (dm_ret_status == SYNCML_DM_SUCCESS && !strServerId.empty()) {
402        LOGD("parseBootstrapServerId returns strServerId: %s", strServerId.c_str());
403        jServerId = jenv->NewStringUTF(strServerId.c_str());
404    }
405
406    return jServerId;
407}
408
409JNIEXPORT jint JNICALL processBootstrapScript(JNIEnv* jenv, jclass, jbyteArray jMsgBuf, jboolean isWbxml, jstring jServerId)
410{
411    SYNCML_DM_RET_STATUS_T dm_ret_status;
412    const char* szDmServerId = jenv->GetStringUTFChars(jServerId, NULL);
413
414    jbyte* jBuf = jenv->GetByteArrayElements(jMsgBuf, NULL);
415    jsize jBufSize = jenv->GetArrayLength(jMsgBuf);
416
417    DmtPrincipal principal("DM_BOOTSTRAP");
418    DMString strServerId(szDmServerId);
419    dm_ret_status = DmtTreeFactory::Bootstrap(
420                    principal, (const UINT8*)jBuf, jBufSize, isWbxml, true, strServerId);
421
422    LOGD("processBootstrapScript dm_ret_status: %d", static_cast<jint>(dm_ret_status));
423
424    return static_cast<jint>(dm_ret_status);
425}
426
427
428JNIEXPORT jbyteArray JNICALL processScript(JNIEnv* jenv, jclass, jstring jServerId,
429        jstring jFileName, jboolean jIsBinary, jint /*jRetCode*/, jobject jdmobj)
430{
431  LOGV("In native processScript\n");
432  g_sessionObj = jdmobj;
433
434  jbyteArray jResult = NULL;
435  SYNCML_DM_RET_STATUS_T ret_status;
436
437  const char* szDmServerId = jenv->GetStringUTFChars(jServerId, NULL);
438
439  if (szDmServerId == NULL) {
440    ret_status = SYNCML_DM_DEVICE_FULL;
441    SET_RET_STATUS_BUF;
442    return jResult;
443  }
444
445  const char* szFileName = jenv->GetStringUTFChars(jFileName, NULL);
446  if (szFileName == NULL) {
447    jenv->ReleaseStringUTFChars(jServerId, szDmServerId);
448    ret_status = SYNCML_DM_DEVICE_FULL;
449    SET_RET_STATUS_BUF;
450    return jResult;
451  }
452
453  LOGV("native processScript reading file <%s>\n", szFileName);
454
455  FILE *fd = fopen(szFileName, "r");
456  if (!fd) {
457    LOGV("native processScript can't open file %s", szFileName);
458    ret_status = SYNCML_DM_FILE_NOT_FOUND;
459    SET_RET_STATUS_BUF;
460    return jResult;
461  }
462
463  // assume 100k is enough
464  const int c_nSize = 100 * 1024;
465  char* szBuf = new char[c_nSize];
466
467  if (szBuf == NULL) {
468    ret_status = SYNCML_DM_DEVICE_FULL;
469    SET_RET_STATUS_BUF;
470    return jResult;
471  }
472
473  int buf_size = fread(szBuf, 1, c_nSize, fd );
474  LOGE("native processScript read %d bytes, jIsBinary=%d\n", buf_size, jIsBinary);
475
476
477  if (buf_size > 0) {
478    DmtPrincipal principal(szDmServerId);
479    DMVector<UINT8> bResult;
480
481    ret_status = DmtTreeFactory::ProcessScript(principal, (const UINT8*)szBuf, buf_size, jIsBinary, bResult);
482
483    // copy bResult to jResult
484    int resultSize = bResult.size();
485
486    if (resultSize > 0) {
487        //Dump((const char*)&bResult.front(), resultSize, jIsBinary);
488        Dump((const char*)bResult.get_data(), resultSize, jIsBinary);
489
490        jResult = jenv->NewByteArray(resultSize);
491
492        //jenv->SetByteArrayRegion(jResult, 0, resultSize, (const jbyte*)&bResult.front());
493        jenv->SetByteArrayRegion(jResult, 0, resultSize, (const jbyte*)bResult.get_data());
494    }
495    else {
496        SET_RET_STATUS_BUF;
497    }
498  }
499  else {
500    // read 0 bytes from script file
501    ret_status = SYNCML_DM_IO_FAILURE;
502    SET_RET_STATUS_BUF;
503  }
504
505  // release memory allocated from GetStringUTFChars
506  jenv->ReleaseStringUTFChars(jServerId, szDmServerId);
507  jenv->ReleaseStringUTFChars(jFileName, szFileName);
508
509
510  LOGV("Native processScript return code %d\n", static_cast<jint>(ret_status));
511  g_sessionObj = NULL;
512
513  return jResult;
514}
515
516
517static JNINativeMethod gMethods[] = {
518    {"initialize", "()I", (void*)initialize},
519    {"destroy", "()I", (void*)destroy},
520    {"parsePkg0", "([BLcom/android/omadm/service/DMPkg0Notification;)I", (void*)parsePkg0},
521    {"startFotaClientSession",
522        "(Ljava/lang/String;Ljava/lang/String;Lcom/android/omadm/service/DMSession;)I",
523        (void*)startFotaClientSession},
524    {"startFotaServerSession", "(Ljava/lang/String;ILcom/android/omadm/service/DMSession;)I",
525        (void*)startFotaServerSession},
526    {"startClientSession", "(Ljava/lang/String;Lcom/android/omadm/service/DMSession;)I",
527        (void*)startClientSession},
528
529    {"startFotaNotifySession",
530        "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/android/omadm/service/DMSession;)I",
531        (void*)startFotaNotifySession},
532
533    {"cancelSession", "()I", (void*)cancelSession},
534
535    {"processScript",
536        "(Ljava/lang/String;Ljava/lang/String;ZILcom/android/omadm/service/DMSession;)[B",
537        (void*)processScript},
538};
539
540int registerNatives(JNIEnv* env)
541{
542    jclass clazz = env->FindClass(javaDMEnginePackage);
543    if (clazz == NULL)
544        return JNI_FALSE;
545
546    if (env->RegisterNatives(clazz, gMethods, sizeof(gMethods)/sizeof(gMethods[0])) < 0) {
547        LOGE("registerNatives return ERROR");
548        return JNI_FALSE;
549    }
550
551    registerDMTreeNatives(env);
552    return JNI_TRUE;
553}
554
555JNIEXPORT jint JNICALL
556JNI_OnLoad(JavaVM* /*vm*/, void* /*reserved*/)
557{
558    LOGD("In JNI_OnLoad");
559    JNIEnv* env = android::AndroidRuntime::getJNIEnv();
560
561    if (env == NULL) {
562        LOGE("Get Environment Error");
563        return -1;
564    }
565
566    return (registerNatives(env) == JNI_TRUE) ? JNI_VERSION_1_6 : -1;
567}
568