Age Verification on Android and iOS, Sideloading for Verified Apps Only

Google is making some changes to Android that make it more restrictive than I’ve previously thought of it. Age verification for apps is coming to Android, and side loading is being restricted to apps that have been “verified” by Google. This is something that is coming to conform to new laws. Apple is also making some changes to conform and published information about it earlier this year. I just received Android’s notification about the change. The full e-mail is appended to the end of this post.

App Verification for Sideloaded Apps

Sideloading is the practice of loading applications onto the device outside of an app store. For Android, this usually involves changing a setting on the phone to allow applications from unknown sources, copying a .apk file to the device, and opening it for the phone to install the app within the .apk. To make an app, someone could download Android Studio, write their code, package their app, and share it with their friends at no cost. Google says that a side-loaded application is 50 times more likely to contain malware. For a developer to distribute their app outside of the Play Store, the developer must register with Google. This is a change from being able to build an app and make the .apk available without interacting with Google. Users who do not load applications outside of the Play Store will not see a difference.

Age Verification

Age verification is attributed to several state laws. The most prominent one referenced is Texas’s Senate Bill 2420. This bill says it regulates the sale of applications to mobile devices. It creates an obligation for app stores to inquire and verify user ages and categorize users into one of a defined set of categories.

  • Age 13 or older but younger than 16
  • At least 16 but younger than 18
  • 18 or older

For each application that a minor downloads, the download will require the consent of the parent. The developers are obligated to come up with age ratings for the applications based on the categories above. The developer must disclose information on the elements of the software that lead to a particular setting being selected. Safety-related features must be enabled in response to the younger age categories. The Texas bill also says that the parent of a minor can make civil damage claims against a developer or app store for failing to meet the requirements.

Expected Impacts

My expectation is that a typical end-user is not likely to notice any change from this bill. At most, there’s the possibility of apps from smaller independent developers that may disappear if it is not eventually updated to conform. From what I’ve seen of the children in my family, they are more inclined to install new apps, especially games. I expect parents to be bugged a lot more about giving permission to install apps. That said, it isn’t unusual for a child to have access to their parents’ phones and sometimes certain passwords. I can’t help but expect that some non-significant portion of children may just use their parents’ phones or passwords to approve themselves.

Different than 1996

These restrictions remind me of another set of restrictions from 29 years ago. In 1996, Congress passed a bipartisan bill, the Communications and Decency Act. A part of that bill required that any website that may have content that isn’t appropriate for children perform age verification and filtering. Most of this bill failed and was enjoined as unconstitutional. The bill’s requirement for age verification would burden lawfully speaking adults and non-commercial interactions. The only portion of that bill that survives as a law today is a law that many simply refer to as Section 230 (47 USC §230). §230 provides a civil defense from liability for what someone else posted to an interactive computer service. The implementation of the Texas bill and others differs from the 1996 Act in that it targets commercial entities (App stores).

The Email from Google

What’s happening

A few U.S. states, currently Texas, Utah, and Louisiana, have recently passed verification laws requiring app stores to verify users’ ages, obtain parental approval, and provide users’ age information to developers. These laws also create new obligations for developers who distribute their apps through app stores in these states.

Our plan to support you

While we have user privacy and trust concerns with these new verification laws, Google Play is designing APIs, systems, and tools to help you meet your obligations. The first verification law to take effect is Texas’s SB 2420 on January 1, 2026. Given short implementation timelines, we are sharing details about the Play Age Signals API (beta) and have made the API integration guide available to you.

What this means for you

These laws impose significant new requirements on many apps that may need to provide age appropriate experiences to users in these states. These requirements include ingesting users’ age ranges and parental approval status for significant changes from app stores and notifying app stores of significant changes. It is important that you review these laws and understand your obligations.

If you have any additional questions, please contact our support team.

Thank you,
Your Google Play team


Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Bluesky: @j2i.net

Running Code in the Pre-Boot Environment

Before your operating system loads, the UEFI (Unified Extensible Firmware Interface) runs. The code in the UEFI is responsible for getting the initial bits of your operating system loaded and then passes control to it for it to load the rest of its components. For the sake of having a better understanding of how things work, this past weekend I decided to write code that would run from the UEFI. Unless you are making system components, this is something that would be impractical to do in the general case. But curious exploration isn’t constrained by practicality. There do exist SDKs for making UEFI programs. Visual Studio also supports the UEFI as a binary target. I’m not using any of the SDKs though. I won’t need them for what I’m doing. To get as far as a “Hello World” I’ll only use Intel Assembler. I wouldn’t suggest that someone that doesn’t already know x86 assembler try this.

Configuring a Visual Studio Project for Assembler

Yes, I’ve not forgotten that I’ve published a video saying that to generally avoid programming in assembler. This isn’t going in any serious code, I don’t have that concern here. From that video, though, I did have instructions on how to create the appropriate type of project in Visual Studio and modify it so that it can compile assembler code.

To use Visual Studio for Assembler development, you will want to create a C++ project. C++ projects support mixing C++ and assembly. We will make a C++ project where 0% of the code is in C++ and 100% is in assembly. In Visual Studio you can create an empty C++ project. Add a new file to the project named main.asm. Right now, VS will not do anything useful with that file. There are two changes that must be made. One is that MASM (Microsoft Assembler) targets must be enabled for the project. The other, MASM must be set as the target for your ASM files. To enable MASM as a target first click on your project in the Solution three, then navigate in Visual Studio to the menu selection “Projects” and then “Build Customizations.” Check the box next to “masm(.targets .props)” and click on “OK.”

To set the target type for main.asm you will need to manually update the project file. right-click on your project in the solution tree and select “Unload project.” Search for “main.asm” in the file. You will find a line that looks like this.

<None Include="main.asm" />

Change the word “None” to “MASM” so that it looks like this.

<MASM Include="main.asm" />

Right-click on the project file and select “Reload project.” Visual Studio able to compile now. But it will try to make a Windows application. That’s not what we want. We want a UEFI application. Visual C++ must be set to target UEFI as the subsystem type. Right-click on the project file and select “Properties.” At the top, change the Configuration setting to “All Configurations.” In the settings tree on the left, navigate to Configuration -> Linker -> System. Change the SubSystem setting to “EFI Application.” Now, select the configuration path Configuration -> Linker -> Advanced. Here, set the “Entry Point” setting to “main”, set “Randomize Base Address” to “No”, and set “Data Execution Prevention” to “No”.

Visual Studio will produce an executable that is the same name as the project but with the EXE extension. That’s not what we want. We want the file name to be BOOTX64.efi. To set that, navigate to the settings path Configuration->Linker->General. Change the Output File setting from $(OutDir)$(TargetName)$(TargetExt) to $(OutDir)BOOTX64.efi. With that, all the configuration steps are complete. Now, we need to write code.

Creating our First Program

This first program will be incredibly simple. It does nothing but loop for some number of cycles and then it terminates. That’s it. Why make a program so simple? Debugging is a little more complex for EFI programs and won’t be covered here. In the absence of a debugger, we will make a program that is as simple as possible while also having effect so that it is known that the program ran. Without relying on any external functionality, the most observable thing that the program can do is take up execution cycles. I do this with the assembler equivalent of

for(auto i=0;i<0x1000000;++i)
{
}

While the program is running, the screen is black. When it finishes running, the UEFI will take over. I can run the program with larger or smaller values in the loop to observe longer or shorter periods of time with a black screen, letting me know that it ran. Here is the source code.

.DATA
; no data
.CODE
main PROC
     MOV RCX, 01000000000H
delay_loop:
	DEC RCX
	JNZ delay_loop
	XOR RAX, RAX
	RET
main ENDP

This code loads a register with a large number. It then loops, decrementing the register value and continuing to do so until that register value has reached zero. When it has, the program sets the RAX register to zero and returns control to the UEFI. The UEFI might check the value of RAX since it is set to a non-zero value if a problem occurred. Compile this and copy the output to a USB key in the folder /EFI/BOOT. It is ready to run!

Running the Program

Usually in Visual Studio, you just press [F5] and your program will compile and run. That’s not an option here. The program must be in a pre-boot environment to run. The easiest way to run the code is either from another computer or from a virtual machine. Attempting to run it on your development machine would mean that you would need to reboot. An emulator or a second machine lets you avoid that. I’m using VM Ware Workstation, which is now available for free from VMWare’s site. ( https://www.vmware.com/products/desktop-hypervisor/workstation-and-fusion ). In any case, you’ll want to ensure that “Secure Boot” is turned off. If Secure Boot is turned on, the computer will refuse to run your code because it isn’t signed with keys that the computer trusts. In VMWare Workstation, right-click on the VM that you plan to use for testing and select “Settings.” In the two tabs at the top of the window that appears, select “Options” and then select “Advanced.” Ensure the firmware type is set to UEFI and that “Secure Boot” is not checked. Click on Okay.

Power-On the Virtual Machine. Once it is powered on, In the “VM” menu select “Removable Devices.” You should see your USB drive. Select it and choose the “Connect Option.” The drive will appear to be unplugged from your computer and connected to the host machine.

Now select the option to reboot the VM.

When the machine reboots, you should see the screen remain black for a bit before the UEFI menu shows. During this period when it is black, the program is looping. If you shut down the VM then the USB drive will become visible to your computer again.

Writing Text

The UEFI has APIs available for reading and writing text, graphics, and more. Let’s try to write text to the screen. We will write “Hello World”, delay, and then return control to the system. This first time that we do that we will be applying knowledge that is known about the system that cannot be inferred from looking at the code alone. When our program starts, two registers already have pointers to a couple of items of information that we need. The RCX register (64-bit general purpose register) has a handle to some Image information. The RDX register has a pointer to the System Table. The system table contains pointers to objects and functions that we will want to use and provides other information about the system. At an offset 0x40 (64) bytes into the system table is a pointer to an object known as ConOut that is used for writing text to the console. At an offset 0x8 bytes from that object’s address is a pointer to the function entry point for the function known as OutputString. We want to call that to display text on the screen. When we call this function, we need to set the RDX register to point to the address of the string that we want to print. After we print, we run our delay and then return control to the UEFI.

.DATA
     szHelloWorld DW 'H','e','l','l','o',' ','W','o','r','l','d','\r','\n', 0
.CODE
main PROC
	SUB RSP, 10H*8	
	MOV RCX, [RDX + 40H] ; Get ConOut function address
	LEA RDX, [szHelloWorld]
	CALL QWORD PTR [RCX + 08H] ;Output String	
	MOV RCX, 01000000000H
delay_loop:
	DEC RCX
	JNZ delay_loop
	ADD RSP, 10H*8	
	XOR RAX, RAX
	RET
main ENDP

END

If we run the program now, it shows text

Adding Structures for Reading Objects

Reading data from arbitrary offsets both work and results in horrible readability. The code will be a lot more readable if we read it using structs instead of arbitrary memory offset. There are three structs that we need: EFI_TABLE_HEADER, SYSTEM_TABLE, and TEXT_OUTPUT_INTERFACE.The EFI_TABLE_HEADER here is being used within the SYSTEM_TABLE struct. I could have defined it in line with SYSTEM_TABLE, but it is used by some other UEFI structures. I decided against it. Most of the other entries in the SYSTEM_TABLE are 64-bit pointers (DQ – or double quads, meaning 8 bytes). Though a few members are doubles (DD – or 32-bit numbers).

EFI_TABLE_HEADER STRUCT
  Signature		DQ ?
  Revision		DD ?
  HeaderSize	DD ?
  CRC			DD ?
  Reserved		DD ?
EFI_TABLE_HEADER ENDS

SYSTEM_TABLE STRUCT
	HDR						EFI_TABLE_HEADER <?> ; 00H
	FIRMWARE_VENDOR_PTR		DQ ? ; 18H
	FIRMWARE_REVISION_PTR	DQ ? ; 20H
	CONSOLE_INPUT_HANDLE	DQ ? ; 28H
	ConIn					DQ ? ; 30H
	CONSOLE_OUTPUT_HANDLE	DQ ? ; 28
	ConOut					DQ ? ; 30
	StandardErrorHandle		DQ ? ; 38
	STD_ERR					DQ ? ; 40
	RuntimeServices			DQ ? ; 48
	BootServices			DQ ? ; 50
	NumberOfTableEntries	DD ? ; 58H
	ConfigurationTable		DQ ? ; 60H
SYSTEM_TABLE ENDS

TEXT_OUTPUT_INTERFACE STRUCT
	Reset				DQ	?
	OutputString		DQ	?
	TestString			DQ	?
	QueryMode			DQ	?
	SetMode				DQ	?
	SetAttribute		DQ	?
	ClearScreen			DQ	?
	SetCursorPosition	DQ	?
	EnableCursor		DQ	?
	Mode				DQ	?
TEXT_OUTPUT_INTERFACE ENDS

With these structs defined, there are a few ways we now have access to access structured data. When using a register to access a field of a struct, I can specify the field offset in the MOV operation. The line of code looks like this.

[RDX + SYSTEM_TABLE.ConOut]

Adding that notation to the code, I end up with code that looks like this.

.CODE
main PROC
	SUB RSP, 10H*8	
	MOV RCX, [RDX + SYSTEM_TABLE.ConOut] ; Get ConOut function address
	LEA RDX, [szHelloWorld]
	CALL QWORD PTR [RCX + TEXT_OUTPUT_INTERFACE.OutputString] ;Output String
	
	MOV RCX, 01000000000H
delay_loop:
	DEC RCX
	JNZ delay_loop
	ADD RSP, 10H*8	
	XOR RAX, RAX
	RET
main ENDP

Reading Text

For someone that wants to play with this, it may also be helpful to be able to read text from the keyboard. Just as there is a Console Output object, there is also a Console Input object. I’ll have the code wait in a spin-loop until a key is pressed. Then it will print the key that was pressed, delay a bit, and terminate. The UEFI boot services do offer a function that will wait on system events. A key press counts as a system event. But I will stick with a spin-wait for simplicity.

I’m declaring a new procedure named waitForKey. This procedure uses a system object that implements the TEXT_INPUT_PROTOCOL. The object has the method ReadKeyStroke that communicates either that there is no keystroke available (sets the RAX register to a non-zero value) or that there is a keystroke (sets RAX register to zero) and writes the keyboard scan code and Unicode character to the memory address that it received in the RDX register. My code loops while RAX is set to non-zero.

.DATA
     szKeyRead DW ' '
.CODE
waitForKey PROC
		SUB RSP, 8
	waitForEnter_Retry:
		MOV RCX, [systemTable]
		MOV RCX, [RCX + SYSTEM_TABLE.ConIn]
		MOV RDX, RSP
		CALL QWORD PTR [ECX+TEXT_INPUT_PROTOCOL.ReadKeyStroke]
		CMP EAX, 0
		JNZ waitForEnter_Retry
		MOV AX, WORD PTR [RSP+2]
		MOV WORD PTR [szKeyRead], AX
		ADD RSP, 8
		RET
waitForKey ENDP

I’ll put the code necessary to print a string in a procedure, too. It will be called printString. The address to the zero-terminated string must be passed in the RAX register.

printString PROC
		MOV RCX, [systemTable]
		MOV RCX, [RCX + SYSTEM_TABLE.ConOut]
		MOV RDX, RAX
		CALL QWORD PTR [RCX+TEXT_OUTPUT_INTERFACE.OutputString]
		RET
printString ENDP

The code will now wait on user input before terminating.

Downloading the Code

If you want to try this out, the Visual Studio project and source code are available on GitHub. In that repository there is also a build folder that contains the binary. If you want to try it out, copy it to the path efi/BOOT on a FAT32 formatted USB drive and boot from it.

Other Resources

I used VMWare for a test device. It is available for free download from the VMWare Workstation web site. For development, I used Microsoft Visual Studio 2022. It is available for free from the Microsoft Visual Studio website. Information about the various objects that are available for use in UEFI code can be found on the site UEFI.org,


Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Bluesky: @j2i.net

Junctions, Hard Links, Symbolic Links on the Windows File System

On windows, the command line tool mklink is used to create symbolic links, junctions, and hard links. But what are those? I’ll first mention a couple of scenarios where they may be helpful. Let’s say that you have a content-driven system. You have multiple versions of your content sets on the file system. Each complete set is in its own folder. The software itself uses the content in a folder named current. When your content syncing applicate gets done downloading a new content set, there are several ways to make it available to the software that is using it. The method I want to focus on is having a virtual folder named current that is actually a pointer to the real folder. A variation of this need is having different versions of an SDK installed. To change from one SDK to another, there could be multiple environment variables that must be updated to point from one SDK version to another. This can be simplified by having a folder that is actually a pointer to the directory that must be used.

Switching from abstract to actual for a moment, I’ve got a couple of versions of the Java Card SDK installed. I just installed the latest version, but I want to keep the previous version around for a while. I’ve got a javacard folder as the root folder of all of the other software used for Java Card development. In it, there are junctions named tools and simulator to point to the Java Card folders for the command line tools and the Java Card simulator. If I need to switch between versions, I only need to delete the old junctions and create new ones.

Arguments for mklink

The arguments to the command are as follows.

mklink[[/j] | [/d] | [/h]] <link> <target>

  • /j – Create a directory junction
  • /d – Create a directory symbolic link
  • /h – Create a hard link (for files only)

Understanding of hard links and junctions requires understanding of the underlying file system. hard links refer to files while junctions refer to directories. Beyond that, they do the same thing. For a hard link or junction, two entries on the file allocation table point to the same inode entries. Symlinks are more like pointers that hold information on the original file system entry. Hard links and junctions can only refer to files on the same file system, while can symlinks refer to a file on a different file system. If no arguments are passed to mklink it will assume that you are making a file symlink.

Command Line Examples

What follows scenarios and the associated commands for those scenarios.

Create a new junction named tools that points to c:\bin\tools_v3.5

mklink/j tools c:\bin\tools_v3.5

Delete a junction named tools.

rd tools

Create a hard link named readme.txt to a file named c:\data\readme_23.txt

mklink /h readme.txt c:\data\readme_23.txt

Delete the hard link for readme.txt.

del readme.txt

What if I Want to Do This with an API Function?

The Win32 API also makes this functionality available to you through the function CreateSymbolicLink and DeviceIoControl.

CreateSymbolicLink

The arguments for the function reflect the arguments used by the command line tool.

BOOLEAN CreateSymbolicLinkW(
  [in] LPCWSTR lpSymlinkFileName,
  [in] LPCWSTR lpTargetFileName,
  [in] DWORD   dwFlags
);

The flag here can be one of three values.

ValueMeaning
0The target is a file
SYMBOLIC_LINK_FLAG_DIRECTORY (0x01)The target is a directory
SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE(0x02)Non-Elevated Create

DeviceIoControl

DeviceIoControl is used for a lot of different functionality. The details of using it in this specific use case may be worthy of its own post. For the sake of brevity, I won’t cover it here. But I’ll mention a few things about using it. When using it to make a junction, the following struct would be used. Note that this struct is a union. The union members that you would use for making a junction to a directory are in the MountPointReparseBuffer.

typedef struct _REPARSE_DATA_BUFFER {
  ULONG  ReparseTag;
  USHORT ReparseDataLength;
  USHORT Reserved;
  union {
    struct {
      USHORT SubstituteNameOffset;
      USHORT SubstituteNameLength;
      USHORT PrintNameOffset;
      USHORT PrintNameLength;
      ULONG  Flags;
      WCHAR  PathBuffer[1];
    } SymbolicLinkReparseBuffer;
    struct {
      USHORT SubstituteNameOffset;
      USHORT SubstituteNameLength;
      USHORT PrintNameOffset;
      USHORT PrintNameLength;
      WCHAR  PathBuffer[1];
    } MountPointReparseBuffer;
    struct {
      UCHAR DataBuffer[1];
    } GenericReparseBuffer;
  } DUMMYUNIONNAME;
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;

Administrative Level Needed for Non-Developers

This functionality usually requires administrative level privileges to execute. However, if a machine has developer mode enabled, the function can be invoked without administrative level privileges. The mklint command line tool appears to follow the same rule. Running this on my own systems (which have developer mode enabled) I can create links without administrative privileges. If you are creating links with a Win32 API call, remember to set the flag SYMBOLIC_LINK_ALLOW_UNPRIVILEGED_CREATE.


Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Bluesky: @j2i.net

iPhone Photo Cage

I get questions about the case on my iPhone frequently enough such that I thought I would write about it so that I have an answer that I can point people to. The case on my iPhone is different than others in that it is made of metal (aluminum, I believe) and has 1/4 inch threaded screw holes for attaching photo accessories. Without anything additional, I can attach it to a tripod in any of the 4 orientations.

my iPhone in a Cage. Note that the back of the iPhone has a sticker on it that reflects the interior.

The cases I have are for the iPhone 13 and the iPhone 13 Pro. Variations of the cases are available for other iPhones too. Though the cases get redesigns with each iteration and don’t look alike. If you’d like to find one for your phone, here are some links. Note that these are Amazon affiliate links. I earn a small commission if you purchase through one of these links.


Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Twitter: @j2inet

Disabling Driver Data Collection (GM Vehicles)

Some recent news articles have made more people aware of data collection for GM vehicles that could, for some, raise insurance rate. The OnStar Safe Driver program collects information about driver habits, such as maximum speeds, instances of hard breaking, hard acceleration, and seatbelt use. This information is collected by GM and available to at least two known data brokers (Lexis Nexis, Verisk) and can end up working it’s way to one’s Insurance company, affecting rates. Two instances of people unhappy with this service include a person in Florida suing both Cadillac and Lexis Nexis after his insurance rates increased and a Chevy Bolt driver whose insurance rates went up.

Did GM Automatically Opt Me Into This Program?

The OnStar Smart Driver Q&A says the following.

Do you auto enroll customers in OnStar Smart Drive?

No, we do not auto enroll customers into OnStar Smart Driver. All customers must opt-in to be enrolled.

https://www.onstar.com/support/faq/smart-driver

How Do I Opt-Out through the App

Depending on your driving style, this could be something that works to your advantage. But in either case, it is good to know how to opt out of it. Many of the GM vehicles have brand-specific variations of an application. For my vehicle, the application is myChevrolet (Android, iOS). Other variants include myGMC (Android, iOS), myBuick (Android, iOS), and myCadillac(Android, iOS).

To turn the feature off, open your GM app and go to the section titled “Trip Overviews and Insights.” (You’ll see “OnStar Smart Driver” listed there). Select it.

One the next screen, click on the geat icon in the upper-right corner to open the settings for this feature.

From there, you’ll sww a switch for turning the feature off. Use this switch to opt out of the program.

What if I don’t have the App Installed?

If you don’t have the application installed but know your GM login, you can unenroll through the website.

From the OnStar SmartDriver Q&A, the steps to perform are as follows.

To unenroll via the vehicle brand website, sign into your account. Click on “Account,” scroll down the page and click on “Data & Privacy.” Scroll down to “OnStar Smart Driver” and select “Manage Settings.” From there, switch off the “OnStar Smart Driver” toggle.

Of you want to know more about the OnStar Driver Safety Program you can read about it here. Though I thought the website was a bit light on information.

I want my collected data removed. What do I do?

You will need to contact Lexis Nexis or Verisk for more information on removal of your data. You can contact them for more information about having your data removed or seeing what data they have on you.


Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Twitter: @j2inet