[Guide] Install Gapps on JingPad (Android ROM)

Hello everyone,

If you want to install gapps on your JingPad, you could follow my guide.
However, I’m not resposible about what you do with your device, so use at your own risk.



Steps:

  1. Download this file:
    https://drive.google.com/file/d/1AZpsbLKXHKs8j7PC2beram0cV7PI6WXH/view?usp=drivesdk
    (md5: 47dba2ca3d42505f2d537f2b853fe6ff)

This is my unofficial build from android source code for JingPad, with several changes.

  1. Unzip it and flash the pac file to your JingPad. In this step all your personal data will be wiped out!

  2. Boot your JingPad, connect it to your computer.

  3. Enter the command on your computer:

adb reboot recovery

Your device will be rebooted into recovery mode.

  1. Choose “mount /system” on your device, then “Apply update from ADB”.

  2. The system partition has about 1.4GB space available. You could download a suitable gapps package and enter the following command:

adb sideload “your-package-path”

I have tested “open_gapps-arm64-10.0-micro-20220215.zip” and “NikGapps-core-arm64-10-20220222-signed.zip”. And it works well.

There will be some error message appear on the screen while installing. Don’t worry, the installation process won’t stop.

  1. After installation, enter the following command on your computer:

adb root
adb disable-verity
adb reboot

Your device will reboot, with gapps installed.

Have a good time!

(Sorry for my bad English)
(I don’t know if it’s necessary to unlock the bootloader, but my device has been unlocked)

(UPDATE: You MUST unlock bootloader first. Don’t use platform-tools r33.0.0, use newest version instead)
(Otherwise when you type adb disable-verity, you will get an error like this:
/system/bin/sh: disable-verity inaccessible or not found)

2 Likes

The changes I made are:

If you want to compile the pac file yourself, the instructions above are enough.

Here are some extra changes I made in my pac file:

UPDATE
I mistyped the package name!
“open_gapps-arm64-10.0-nano-20220215.zip” works well, as my picture shows
Not micro!!!
The “pixel launcher” app in micro keeps crashing
So if you want to install micro, you may need to remove pixel launcher first
To avoid that I recommend installing nano

Thank you very much! Your guidance and work have been invaluable in making this device usable.

Thank you for this.

If anyone needs to change to english. It can be done by editing the following file.

/source/device/sprd/roc1/ud710_2h10u/jingos/device.mk

edit the following lines.

persist.sys.language=en
persist.sys.country=US
persist.sys.timezone=America/New_York
persist.sys.locale=en-US
ro.product.locale.language=en
ro.product.locale.region=US

I was able to remove some of the apps by editing the same file modify the following lines adding a “#” at the beginning.

PRODUCT_PACKAGES +=
.# JingOta
.# jl-install.sh
.# JingosLog
.# JingCenter
.# com.yozo.office
.# com.asa.jingnote
.# JingSettings

Once I made these changes I used the following to create the .pac file without the debug.

cd /source/vendor/sprd/release/IDH/Script
./build_pac.sh -a ud710_3h10u_native-user-native -b all 2>&1 | tee build.log

  • I do not know if removing these packages breaks anything, if it does them you’ll have to recompile and flash your device. do at your own risk.

An easiest way is to just install google play services and then you can add an external language in the settings without having to recompile the custom rom.

You don’t have to do a custom rom, but if you want all the jing apps gone you will need to.

  • I do not know if removing these packages breaks anything, If it does then add them back in and recompile then flash your device. do at your own risk.

I was able to remove the Jing specific apps by editing the following file adding a “#” at the beginning.
source/device/sprd/roc1/ud710_2h10u/jingos/device.mk

PRODUCT_PACKAGES +=
.# JingOta
.# jl-install.sh
.# JingosLog
.# JingCenter
.# com.yozo.office
.# com.asa.jingnote
.# JingSettings

If anyone needs to change default to english. It can be done by editing the following lines.

persist.sys.language=en \
persist.sys.country=US
persist.sys.timezone=America/New_York
persist.sys.locale=en-US
ro.product.locale.language=en
ro.product.locale.region=US

How I removed the Jing Chinese specific apps, I added a “#” after the “+=”
source/device/sprd/roc1/ud710_2h10u/jingos/apps/preinstall/device.mk

PRODUCT_PACKAGES += #com.tencent.qqlivepad
PRODUCT_PACKAGES += #com.ss.android.article.news
PRODUCT_PACKAGES += #com.tencent.mm

source/device/sprd/roc1/ud710_2h10u/jingos/apps/postinstall/device.mk

PRODUCT_COPY_FILES += #device/sprd/roc1/ud710_2h10u/jingos/apps/postinstall/com.baidu.input.apk:system/jlpreinstall/app/com.baidu.input
PRODUCT_COPY_FILES += #device/sprd/roc1/ud710_2h10u/jingos/apps/postinstall/应用宝.apk:system/jlpreinstall/app/com.tencent.android.qqdownloader

The edit’s mentioned apear to be done in the following files.

source/bootable/recovery/install/install.cpp

//Commented_out_below
// return INSTALL_CORRUPT;
//Commented_out_above
}
bool is_ab = android::base::GetBoolProperty(“ro.build.ab_update”, false);
// Verifies against the metadata in the package first.
//Commented_out_below
/*
if (int check_status = is_ab ? CheckPackageMetadata(metadata, OtaType::AB) : >
check_status != 0) {
log_buffer->push_back(android::base::StringPrintf(“error: %d”, kUpdateBinar>
return check_status;
}
*/
//Commented_out_above

// Verify package.
ui->Print(“Verifying update package…\n”);
auto t0 = std::chrono::system_clock::now();
int err = verify_file(package, loaded_keys);
std::chrono::duration duration = std::chrono::system_clock::now() - t>
ui->Print(“Update package verification took %.1f s (result %d).\n”, duration.>
if (err != VERIFY_SUCCESS) {
LOG(ERROR) << “Signature verification failed”;
LOG(ERROR) << "error: " << kZipVerificationFailure;
//Commented_out_below
// return false;
//Commented_out_above

source/bootable/recovery/etc/init.rc

service recovery /system/bin/recovery
class recovery
socket recovery stream 422 system system
//Commented_out_edit_below
.# seclabel u:r:recovery:s0
seclabel u:r:su:s0
//Commented_out_edit_above

source/device/sprd/roc1/ud710_3h10u/BoardConfig.mk

BOARD_SYSTEMIMAGE_PARTITION_SIZE := 4169728000

source/packages/apps/JingSettings/res/values/arrays.xml

<string-array name="screen_timeout_entries">
    <item>1500000 seconds</item>
    <item>30 seconds</item>
    <item>1 minute</item>
    <item>2 minutes</item>
    <item>5 minutes</item>
    <item>10 minutes</item>
    <item>30 minutes</item>
</string-array>

<!-- Do not translate. -->
<string-array name="screen_timeout_values" translatable="false">
    <!-- Do not translate. -->
    <item>1500000000</item>

source/packages/apps/JingSettings/res/values/jing_strings.xml

string name=“jing_os_version” translatable=“false”>JingOS 1.0

source/device/sprd/roc1/ud710_3h10u/ud710_3h10u_native.mk

#internal software version
SOFTWARE_INTERNAL_VERSION :=1.0.0002.013

Once I made these changes I used the following to create the .pac file.

cd source/vendor/sprd/release/IDH/Script
./build_pac.sh -a ud710_3h10u_native-userdebug-native -b all 2>&1 | tee build.log
or
./build_pac.sh -a ud710_3h10u_native-user-native -b all 2>&1 | tee build.log

This is the try_update_binary function:

// If the package contains an update binary, extract it and run it.
static int try_update_binary(const std::string& package, ZipArchiveHandle zip, bool* wipe_cache,
                             std::vector<std::string>* log_buffer, int retry_count,
                             int* max_temperature, RecoveryUI* ui) {
  static constexpr const char* UPDATE_BINARY_NAME = "META-INF/com/google/android/update-binary";
  std::map<std::string, std::string> metadata;
  if (!ReadMetadataFromPackage(zip, &metadata)) {
    LOG(ERROR) << "Failed to parse metadata in the zip file";
    //return INSTALL_CORRUPT;
  }

  bool is_ab = android::base::GetBoolProperty("ro.build.ab_update", false);
  // Verifies against the metadata in the package first.
  /*
  if (int check_status = is_ab ? CheckPackageMetadata(metadata, OtaType::AB) : 0;
      check_status != 0) {
    log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure));
    return check_status;
  }
  */

  ReadSourceTargetBuild(metadata, log_buffer);

  // The updater in child process writes to the pipe to communicate with recovery.
  android::base::unique_fd pipe_read, pipe_write;
  // Explicitly disable O_CLOEXEC using 0 as the flags (last) parameter to Pipe
  // so that the child updater process will recieve a non-closed fd.
  if (!android::base::Pipe(&pipe_read, &pipe_write, 0)) {
    PLOG(ERROR) << "Failed to create pipe for updater-recovery communication";
    return INSTALL_CORRUPT;
  }

  // The updater-recovery communication protocol.
  //
  //   progress <frac> <secs>
  //       fill up the next <frac> part of of the progress bar over <secs> seconds. If <secs> is
  //       zero, use `set_progress` commands to manually control the progress of this segment of the
  //       bar.
  //
  //   set_progress <frac>
  //       <frac> should be between 0.0 and 1.0; sets the progress bar within the segment defined by
  //       the most recent progress command.
  //
  //   ui_print <string>
  //       display <string> on the screen.
  //
  //   wipe_cache
  //       a wipe of cache will be performed following a successful installation.
  //
  //   clear_display
  //       turn off the text display.
  //
  //   enable_reboot
  //       packages can explicitly request that they want the user to be able to reboot during
  //       installation (useful for debugging packages that don't exit).
  //
  //   retry_update
  //       updater encounters some issue during the update. It requests a reboot to retry the same
  //       package automatically.
  //
  //   log <string>
  //       updater requests logging the string (e.g. cause of the failure).
  //

  std::vector<std::string> args;

  is_ab = false;
  ZipString binary_name(UPDATE_BINARY_NAME);
  ZipEntry binary_entry;
  if (FindEntry(zip, binary_name, &binary_entry) != 0) {
    LOG(ERROR) << "Failed to find update binary " << UPDATE_BINARY_NAME;
    is_ab = true;
  }

  if (int update_status =
          is_ab ? SetUpAbUpdateCommands(package, zip, pipe_write.get(), &args)
                : SetUpNonAbUpdateCommands(package, zip, retry_count, pipe_write.get(), &args);
      update_status != 0) {
    log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure));
    //return update_status;
  }

  pid_t pid = fork();
  if (pid == -1) {
    PLOG(ERROR) << "Failed to fork update binary";
    log_buffer->push_back(android::base::StringPrintf("error: %d", kForkUpdateBinaryFailure));
    return INSTALL_ERROR;
  }

  if (pid == 0) {
    umask(022);
    pipe_read.reset();

    // Convert the std::string vector to a NULL-terminated char* vector suitable for execv.
    auto chr_args = StringVectorToNullTerminatedArray(args);
    execv(chr_args[0], chr_args.data());
    // We shouldn't use LOG/PLOG in the forked process, since they may cause the child process to
    // hang. This deadlock results from an improperly copied mutex in the ui functions.
    // (Bug: 34769056)
    fprintf(stdout, "E:Can't run %s (%s)\n", chr_args[0], strerror(errno));
    _exit(EXIT_FAILURE);
  }
  pipe_write.reset();

  std::atomic<bool> logger_finished(false);
  std::thread temperature_logger(log_max_temperature, max_temperature, std::ref(logger_finished));

  *wipe_cache = false;
  bool retry_update = false;

  char buffer[1024];
  FILE* from_child = android::base::Fdopen(std::move(pipe_read), "r");
  while (fgets(buffer, sizeof(buffer), from_child) != nullptr) {
    std::string line(buffer);
    size_t space = line.find_first_of(" \n");
    std::string command(line.substr(0, space));
    if (command.empty()) continue;

    // Get rid of the leading and trailing space and/or newline.
    std::string args = space == std::string::npos ? "" : android::base::Trim(line.substr(space));

    if (command == "progress") {
      std::vector<std::string> tokens = android::base::Split(args, " ");
      double fraction;
      int seconds;
      if (tokens.size() == 2 && android::base::ParseDouble(tokens[0].c_str(), &fraction) &&
          android::base::ParseInt(tokens[1], &seconds)) {
        ui->ShowProgress(fraction * (1 - VERIFICATION_PROGRESS_FRACTION), seconds);
      } else {
        LOG(ERROR) << "invalid \"progress\" parameters: " << line;
      }
    } else if (command == "set_progress") {
      std::vector<std::string> tokens = android::base::Split(args, " ");
      double fraction;
      if (tokens.size() == 1 && android::base::ParseDouble(tokens[0].c_str(), &fraction)) {
        ui->SetProgress(fraction);
      } else {
        LOG(ERROR) << "invalid \"set_progress\" parameters: " << line;
      }
    } else if (command == "ui_print") {
      ui->PrintOnScreenOnly("%s\n", args.c_str());
      fflush(stdout);
    } else if (command == "wipe_cache") {
      *wipe_cache = true;
    } else if (command == "clear_display") {
      ui->SetBackground(RecoveryUI::NONE);
    } else if (command == "enable_reboot") {
      // packages can explicitly request that they want the user
      // to be able to reboot during installation (useful for
      // debugging packages that don't exit).
      ui->SetEnableReboot(true);
    } else if (command == "retry_update") {
      retry_update = true;
    } else if (command == "log") {
      if (!args.empty()) {
        // Save the logging request from updater and write to last_install later.
        log_buffer->push_back(args);
      } else {
        LOG(ERROR) << "invalid \"log\" parameters: " << line;
      }
    } else {
      LOG(ERROR) << "unknown command [" << command << "]";
    }
  }
  fclose(from_child);

  int status;
  waitpid(pid, &status, 0);

  logger_finished.store(true);
  finish_log_temperature.notify_one();
  temperature_logger.join();

  if (retry_update) {
    return INSTALL_RETRY;
  }
  if (WIFEXITED(status)) {
    if (WEXITSTATUS(status) != EXIT_SUCCESS) {
      LOG(ERROR) << "Error in " << package << " (status " << WEXITSTATUS(status) << ")";
      return INSTALL_ERROR;
    }
  } else if (WIFSIGNALED(status)) {
    LOG(ERROR) << "Error in " << package << " (killed by signal " << WTERMSIG(status) << ")";
    return INSTALL_ERROR;
  } else {
    LOG(FATAL) << "Invalid status code " << status;
  }

  return INSTALL_SUCCESS;
}

And in rootfs of recovery copy /system/bin/sh to /sbin/sh after you compile
Then:

./build_pac.sh -a ud710_3h10u_native-user-native -b remake 2>&1 | tee build.log

Notice that you can’t use gapps now. You will get an error like “there is a problem contacting with google server” if you try to login. I don’t know why this happens. :slightly_frowning_face:

Since the google store won’t work because I can’t login. I get the same error “There is a problem contacting with google server”. I went a different route. I added the store from https://apkpure.com/. To make sure the store it would be there any time I wanted to refresh my system I added it to the build process.

source/device/sprd/roc1/ud710_2h10u/jingos/jingos_whiteList

apkpure.v3.17.52

source/device/sprd/roc1/ud710_2h10u/jingos/apps/postinstall/devices.mk

PRODUCT_COPY_FILES += device/sprd/roc1/ud710_2h10u/jingos/apps/postinstall/apkpure.v3.17.52.apk:system/jlpreinstall/app/apkpure.v3.17.52

I placed the downloaded APK file in the following directory.

source/device/sprd/roc1/ud710_2h10u/jingos/apps/postinstall/

I also added other apps using this same process that I know are standard for me and i’ll be using over the next few years to my build.

notes:
The APK file name has to be the same as the entry in these other two files or you’ll get an error when building.

I found I had to allow “jl-install.sh” for the apps to get installed.

How did you install the gapps?
I tried again today, and finally found how I was able to use gapps before.

  • You should have booted to the system once, before you boot to recovery and flash gapps.

  • “open_gapps-arm64-10.0-pico-20220215.zip” and “open_gapps-arm64-10.0-nano-20220215.zip” are probably the only two available version that work.

  • After reboot, you should get an error like “This device isn’t Play Protect certified” which keeps popping up, not “There is a problem contacting with google server”.

Steps:

  • Go to play store, click sign in;
  • It said “This device isn’t Play Protect certified”, click that green link “g.co/AndroidCertifiedDevice”;
  • Follow Google’s instructions to register your device;
  • Go to Settings, try your best to force stop gms & play store and clean their data;
    (But do NOT clean gsf’s data! Clean cache is enough)
  • Go to play store and sign in before gms know it.

Here is a video that shows the process:

Good Luck!