03 Oct 2026
TalkAndroid
The Best Nothing Phone (4a) Screen Protectors
The Nothing Phone 4(a) ships with Gorilla Glass 7i and a plastic screen protector already. But it won't last, so here's what to do when it wears out.
03 Oct 2026 12:15pm GMT
The Best Google Pixel 11 Pro XL Screen Protectors
From a $9 spare pack to a $50 warranty pick, your Pixel 11 Pro's screen has the best options for added protection.
03 Oct 2026 10:32am GMT
Boba Story Lid Recipes – 2026
Look no further for all the latest Boba Story Lid Recipes. They are all right here!
03 Oct 2026 10:10am GMT
02 Oct 2026
Android Developers Blog
Device Streaming and Android skills - available in Android CLI

As Android developers, you have many choices when it comes to the agents, LLMs, tools, and command-line interfaces (CLI) you use for app development. Our goal is to help you build beautiful, high-quality Android apps, no matter how you choose to build. We're announcing updates to our Android CLI command-line tooling, including the ability to access real devices through Android Device Streaming. We're also sharing more Android skills, and giving you a deep dive into the Wear OS Compose Material 3 skill.
Android CLI for command-line development
Android CLI is our tool to make command-line interface Android development easier. It supports any AI agent or tool in building more efficiently for Android.
Along with the android-cli skill, your agents use Android CLI to create, build, test, and manage Android projects, help you set up the development environment, create and run emulators, and execute test runs.
Android Device Streaming now available in CLI
It's important to test your app on real devices to catch issues that are hardware- or OS-specific, but sometimes you don't have access to the ones you need. Android Device Streaming gives you access to real physical devices, remotely. Your agent can now use Android Device Streaming anywhere, through Android CLI.

The agent can interact with physical devices (as if they were plugged in over USB) over a secure ADB over SSL connection. This enables spinning up devices, deploying builds, collecting logs and traces, and even capturing screenshots headlessly-all through the terminal.
To get started, link your project, instruct your agent to list available remote devices and decide which device you want next. Read the documentation to learn more, and see the Android CLI release notes.
Grounding agents with Android skills
To bridge the gap between LLMs' default knowledge and platform-specific standards and updates, we keep growing our Android skills repository.
Android skills are structured instructions (SKILL.md files) that ground AI agents with our official guidance from developer.android.com. Instead of relying on a model's training cutoff, skills provide more precise and fresher data, API references and samples, and architectural patterns directly into your agent's context.
With over 20 skills available, you can now equip your AI agents to handle more complex and specialized development tasks such as:
- Audit Play policy compliance: Audit app manifests, runtime permissions, target SDK levels, and privacy disclosures before submitting your app for Play Policy review.
- Implement Restore Credentials: Implement re-authentication across device setup and cloud restores using Jetpack Credential Manager.
- Android Intent security: Detect and prevent implicit intent hijacking, secure broadcast receivers, and validate PendingIntent declarations.
- Use Android profilers: Diagnose UI frame drops, interpret CPU/memory traces, and query trace data using natural language mapped to PerfettoSQL.
- CameraX: Replace legacy Camera1/Camera2 code with lifecycle-aware CameraX.
- Migrate Leanback to Compose for TV: Modernize Android TV experiences by transitioning from Leanback to Compose for TV.
- Integrate Media3 Cast: Connect Jetpack Media3 media sessions with Google Cast receiver devices and sync playback states.
- Integrate Play Engage SDK: Integrate the Google Play Engage SDK to publish user recommendations and cluster surfaces.
- Set up testing strategy: Configure unit test suites, Compose UI testing rules, and screenshot testing infrastructure.
- Audit R8 configuration: Optimize your app's performance by auditing your R8 configuration.
Our Android skills are thoroughly evaluated. To understand the philosophy and methodology behind this project, as well as why all skills should come with evals, make sure to read Inside Android Skills - Built for deprecation.
Managing skills across projects and individual agent directories is pretty straightforward with Android CLI:
- Install Android CLI
- Run
android initto install theandroid-cliskill - To list all available official skills, run:
android skills list - To install individual skills into a single project root:
android skillsaddwear-compose-m3 --project=. - To update skills, use:
android skills update --allandroid skills update wear-compose-m3(for an individual skills)
Skills are designed to be environment-agnostic. From writing code in Android Studio and Antigravity, to pairing with third-party agents like Claude and Codex, our Android skills work across your entire setup.
.gif)
Skill spotlight: Wear Compose Material 3
Building for Wear OS means distinct design and development decisions: round viewports, rotary input, ambient display mode, minimizing power consumption, preferring TransformingLazyColumn, and using the AppScaffold and ScreenScaffolds containers.
Without explicit guidance, LLMs lack the understanding and knowledge of these distinct patterns that make the Wear apps really stand out and shine.
To help agents with this, we released the Wear Compose Material 3 skill (wear/wear-compose-m3).
Check out this video for more information on how powerful this skill is:
Early adopters are already seeing significant productivity impact with this skill. The engineering team at FotMob used it for tasks like modernizing their existing Wear M3-based app, migrating multiple lists to TransformingLazyColumn with ScreenScaffold content padding, ListHeader titles, SurfaceTransformation on cards and buttons, theme typography, and Wear previews.
The result? The changes compiled successfully and were verified on the emulator for scrolling, rotary, edge morphing and RTL. This enabled the team to delete their legacy wrapper, and all rotary and focus boilerplate.
The skill caught mistakes that the underlying model missed, such as forgetting to forward ScreenScaffold's contentPadding into the list, and using theme typography over hardcoded sp.
"One skill, one afternoon, eight lists migrated and a pile of custom rotary code gone!" - Roy Solberg, Android Tech Lead at FotMob.
Get started today
When you're ready to get started, install Android CLI with just one command. You can learn more about Android CLI by reading the developer documentation, and as well as checking out the latest updates in the Android CLI release notes.
After installing Android CLI, run android init to configure your agent, then browse the latest Android skills available for use and install them with android skills add.
Also check out the Android Device Streaming documentation to learn more about how this feature allows you to access real devices on the cloud inside both Android Studio and via your agents.
02 Oct 2026 4:00pm GMT
30 Sep 2026
Android Developers Blog
How Instagram Direct engineers built AI-native UI architecture with Jetpack Compose and reduced token cost per agent session by 33%
Posted by Pavlo Stavytskyi, Software Engineer, Meta and Rebecca Franks, Developer Relations Engineer, Google
This blog post is written in collaboration with the Meta team.
Instagram Direct is one of the core surfaces on Instagram, handling billions of user messages every single day. Over years of iteration, the team squeezed every micro-optimization possible out of the legacy Android View system. However, maintaining and expanding a heavily optimized legacy surface creates significant technical debt and engineering overhead, especially as teams increasingly adopt declarative UI and AI coding assistants.
Adopting Jetpack Compose for Instagram Direct went beyond a typical UI modernization. The team built an AI-native UI codebase that is 50% smaller than the original implementation, while achieving a 35% reduction in AI agent execution time, 32% fewer engineer-agent exchanges, and a 33% reduction in token cost. In close partnership with Google, the team adopted Jetpack Compose while maintaining a high performance bar. Through the performance optimizations, Meta and Google improved Compose not only for Instagram, but for the broader Android developer ecosystem too.
Modernizing the codebase at massive scale
AI has rapidly become a daily companion for engineers in the industry, and applying it to a large-scale codebase like Instagram already yields real productivity gains. The Instagram Direct team set a more ambitious goal. Rather than simply pointing AI tools at the existing code, the team redesigned the codebase and its architecture to be AI-native by design, multiplying the impact of AI far beyond what retrofitting alone can deliver.
The Instagram Direct team chose Jetpack Compose as a key component for building an AI-native UI architecture. Its declarative nature ensures code is concise, predictable, and structurally easier for AI models to reason about, with fewer side effects, less implicit state, and clearer component boundaries.
The migration to Jetpack Compose required careful planning. Hundreds of millions of people send messages on Instagram every day, so the migration had to be gradual, smooth, with zero disruption to the experience while the team re-architected the foundation underneath it. To illustrate the scale of the challenge: Individual UI components can render in over 160 distinct state permutations, and a single conversation screen alone handles more than 200 distinct message types.
When migrating a codebase of this size to Compose, it's tempting to take the easy way out and embed Compose UI components inside the existing View hierarchy. As an incremental step during a gradual migration, that's perfectly valid. Over the long run, though, integrating Compose inside a View-based codebase poses a challenge. AI tools often take the path of least resistance. If you mix declarative and imperative UI code, AI is likely to blend them incorrectly, introducing subtle bugs, tech debt and performance regressions.
Building an AI-native UI architecture
At the scale of Instagram, a degree of architectural abstraction is unavoidable, and it is what keeps the app maintainable as it grows. Consider a common pattern, where every RecyclerViewitem type is modeled as a descendant of a custom RecyclerView base class that exposes usual lifecycle hooks such as onBind.
Example 1
class ChatItem(
val features: FeatureFlagProvider
) : RecyclerViewItem<ComposeViewHolder, ChatUiState> {
// Imperative context:
// AI could often take the path of least resistance and generate a mutable
// state here, dispatched outside the ChatUiState. This class survives
// re-bindings and is shared across multiple items, ultimately leading to
// unexpected, hard-to-reproduce bugs.
var isPinned: Boolean = false
override fun onBind(holder: ComposeViewHolder, uiState: ChatUiState) {
// Imperative context
val isPinnedChatsEnabled = features.isEnabled("pinned_chats_feature")
// Declarative context
holder.composeView.setContent {
// Blending imperative and declarative contexts
if (isPinnedChatsEnabled) {
Button(onClick = { isPinned = !isPinned }) {
Text(if (isPinned) "Unpin" else "Pin")
}
}
...
}
}
}
In the snippet above, two problems creep in. First, the isPinnedChatsEnabled flag is read in imperative code and then captured inside a Compose lambda, a subtle coupling across paradigms. Second, isPinned lives as a mutable field on the item itself rather than in ChatUiState, so it survives RecyclerView re-binding and recycling across rows, leaking and producing bugs that are painful to reproduce.
Even when the code is cleaned up by giving the item a dedicated @Composable function, the same problems remain.
Example 2
class ChatItem(
val features: FeatureFlagProvider
) : ComposeRecyclerViewItem<ChatUiState> {
// Imperative context
val isPinnedChatsEnabled = features.isEnabled("pinned_chats_feature")
var isPinned: Boolean = false
// Declarative context
@Composable
override fun Content(uiState: ChatUiState) {
// Blending imperative and declarative contexts
if (isPinnedChatsEnabled) {
Button(onClick = { isPinned = !isPinned }) {
Text(if (isPinned) "Unpin" else "Pin")
}
}
...
}
}
This is deliberately a simple example, but it illustrates a broader issue- the fewer boundaries AI is given, the lower the quality of the code it produces over time. Guardrails and skills help, but they are not enough on their own, because when AI hits friction it will often route around them to unblock itself.
To make the codebase AI-friendly, it needs to follow two practical rules:
- Minimize dependency on custom context. The more bespoke, codebase-specific knowledge an AI agent needs to make a correct change, the lower the quality of its output. The closer the codebase is to known best practices, the better the AI results.
- An AI-first codebase must enforce its own boundaries. Patching design gaps with AI skills doesn't scale, since every skill loaded into context costs tokens and can degrade the agent's performance. Instead, the architecture itself should carry that weight. AI agents naturally take the path of least resistance, so the design should make that path lead to correct, high-quality code, while making poor design decisions hard and expensive to express.
A list item can still be represented by its own abstraction, but in this case all the Compose code lives in the constructor, so it has no access to class members or state, and its only source of arguments is the constructor. This makes it equivalent to a plain @Composable function, while conforming to the existing architecture.
Example 3
class ChatItem(
val features: FeatureFlagProvider,
val onPin: (Boolean) -> Unit,
) : ComposeItem<ChatUiState>(
// Compose UI
content = { uiState: ChatUiState ->
val isPinnedChatsEnabled = features.isEnabled("pinned_chats_feature")
if (isPinnedChatsEnabled) {
Button(onClick = { onPin(!uiState.isPinned) }) {
Text(if (uiState.isPinned) "Unpin" else "Pin")
}
}
...
},
)
Migrating a codebase of this size is a massive undertaking. For a long time, the hundreds of UI components that make up the majority of Direct UI had to coexist with their legacy counterparts, with both maintained in parallel. AI workflows helped make this parallel migration possible by speeding up the process of writing massive amounts of code. That approach is what let the Direct team perform the migration in record time, all without disrupting the rest of the team, who kept shipping the features that improve the experience of millions of people every day.
Multiple engineers ran their own AI agents against a shared knowledge base of reusable skills and conventions built during the migration. That kept workflows and best practices in sync across the team, rather than having each engineer rediscover them. Within each surface, the team performed the migration in the following stages:
- Write all the Compose code with AI.
- Polish it, handling edge cases and closing performance gaps, until the UI was rolled out to real users in a public test.
Splitting the work into two stages per screen lets one engineer move quickly through the entire surface, settling the architecture and the tricky edge cases up front. With that groundwork in place, others can focus on getting the UI production-ready without stopping to make those technical decisions themselves, keeping the overall migration fast.
The results of the migration validated the approach. For migrated Instagram Direct surfaces, Jetpack Compose allowed the team to reduce the total amount of UI code by 50%. Less code for AI to generate is associated with higher-quality output and lower token cost per task.
An internal data analysis of the Android codebase for Instagram Direct compared AI agent sessions working on Compose UI against the same tasks using Android Views. The efficiency gains were clear across two dimensions:
- Per character of landed code: Compose required 32% fewer engineer-agent exchanges and 35% less agent execution time (the elapsed time from when an agent starts working on an engineer's request until it returns a response).
- Per agent session: The overall token cost dropped by 33% with Compose in comparison to Views.
We report both output efficiency and typical session numbers because they are independently useful outcomes. The engineer-agent exchanges and execution time figures compare resource use per unit of landed output, while the token figure compares total cost for a typical agent session.
The data also revealed a consistent difference in how the two frameworks handle complex or fragile code. Meta tracks this using a risk score of code changes, which evaluates overall code quality and the likelihood of a change causing production incidents. The analysis measured an agent's resource efficiency using a composite of token consumption, agent's execution time and the engineer to agent interactions. As files accumulate a higher risk score, AI agent sessions naturally become less resource efficient.
When a file's accumulated risk score doubles, the UI implemented with Android Views reduces agent resource efficiency by 30% (per landed character). In the same circumstances, the reduction by Jetpack Compose UI is only 9%.
Through partnership between Google and Meta, the Instagram Direct team brought a fresh perspective to Compose adoption - approaching it through the lens of the codebase's AI-readiness, not just a UI rewrite. This work revealed Compose's strength in serving as a foundation for building AI-first codebases and architectures, especially when applied at the scale of apps like Instagram.
Performance optimizations
Instagram Direct is one of the most integral surfaces of the app, and people expect it to feel fast and responsive at all times. Adopting Jetpack Compose effectively meant a substantial UI rewrite, and the number-one goal was to preserve the high-quality experience with no regressions.
Years of iteration had already pushed the legacy View-based implementation on Instagram to an exceptionally high performance bar, and the team needed to meet that same standard while moving to an entirely new UI framework.
Instagram measures hundreds, if not thousands, of performance metrics. For Compose adoption, the following three were the most important:
- Time to interact - the amount of time between opening the screen to being able to use it.
- Time to fully load - the amount of time between opening the screen to when all the content is fully loaded (i.e. images).
- Scroll performance - how smoothly the screen scrolls, without dropped frames.
These metrics are tracked at runtime in production, making it possible to run A/B tests comparing the migrated Compose UI against the legacy UI and evaluate the performance impact of this effort.
A common way to approach a migration like this is to start small, moving over a handful of UI components, gathering data, and studying how they behave. While helpful, these early results only paint a partial picture, providing false negatives against Compose adoption because:
- Not representative - one migrated UI component can provide useful data about its overall performance on a particular screen. However, different components behave differently for reasons that don't generalize, so you can't always extrapolate from it.
- Interop cost - a small Compose piece inside a big View codebase pays an unpredictable bridging cost between the two systems. That overhead distorts the measurement, so early small-scale results don't reflect what full migration would actually look like.
The result is that small migrations, while useful, don't always reflect the full impact of Compose. The more of a surface is migrated end-to-end without bridging interruptions, the clearer and better the picture becomes performance-wise.
The core screens in Instagram Direct are built around long lists of varied item types, originally implemented with RecyclerView. The architecture relies on custom abstractions for scalability, but it remains bound to the lifecycle of the View-based system.
The team's primary undertaking was a gradual migration of several hundred individual list items to Compose within the existing RecyclerView-based architecture, rolling them out in production in small independent groups under A/B tests - all of it without visible changes to the user's messaging experience.
The biggest downside of such a setup is a significant dependency on the legacy View system through a core RecyclerView architecture, even after the full migration of every list item to Compose. As a natural next step, the team decided to invest in replacing the RecyclerView-based core architecture with the Compose-native alternative - LazyColumn.
This means Compose UI components should be abstracted away from the framework they are enclosed in while still being compatible with both RecyclerView and LazyColumn at the same time. Equally important is the ability to switch between the two at runtime via feature flags, to enable A/B testing.
While the new Compose items are natively compatible with LazyColumn and can be plugged into an uninterrupted composition tree, an interop API was created to slot them into a RecyclerView as well. This made it possible to roll out the LazyColumn setup under an A/B test side-by-side with RecyclerView - reusing the same Compose items and polishing performance, without disrupting the rest of the team building and refining features.
The scale, complexity and sensitivity of Instagram to even the smallest regressions posed a unique challenge for Jetpack Compose. Addressing these required an iterative, hands-on partnership. Working closely together, Google and Meta engineers analysed metrics to pinpoint and design new Compose capabilities to meet or exceed the View-based benchmarks. As a result of this partnership, the following additions to Jetpack Compose stand out: Pausable composition with LazyLayoutCacheWindows and visibility tracking.
Pausable composition with LazyLayoutCacheWindows
Pausable composition (enabled by default in Compose 1.10) allows expensive lazy-list items to be composed incrementally across frames to prevent jank. When paired with LazyLayoutCacheWindow (added in Compose 1.9), the combination significantly improves scroll smoothness. In recent internal testing at Meta, combining Pausable composition with a one-viewport LazyLayoutCacheWindow reduced large frame drops per minute (LFDs/m) by about 13% compared with vanilla Compose. Cache Window on its own reduced it by about 8% against the same baseline. LFDs/m is an internal metric Meta uses to track noticeable stutters while scrolling.
Using a LazyLayoutCacheWindow in your app prepares and retains off-screen items within a pixel-based band around the viewport to enable fast flings. To take advantage of LazyLayoutCacheWindows in your app, you can use the latest Compose 1.13.0-alpha03 and set it up as shown in the example below:
val cacheWindow = LazyLayoutCacheWindow(ahead = 150.dp, behind = 100.dp)
// OR
val cacheWindow = LazyLayoutCacheWindow(aheadFraction = 0.5f, behindFraction = 0.3f)
LazyColumn(state = state, cacheWindow = cacheWindow) {
...
}
There are two ways to configure the cache window. Both describe the same thing: how much off-screen content to keep composed, but in different units.
- Dp: fixed absolute length.
ahead = 150.dpkeeps 150dp of content composed past the visible edge regardless of device. - Float: fraction of the viewport.
aheadFraction = 0.5fkeeps half a screen composed ahead, so the absolute amount scales with screen height supporting various form factors: more on a tablet or unfolded foldable, less on a compact phone.
The Instagram team fine-tuned the cache window's float fractions specifically for Direct's content structure and item sizes. Since the ideal values vary depending on the specific UI parameters, finding the right balance requires some experimentation.
Impression logging with onVisibilityChanged
The onVisibilityChanged (added in Compose 1.9.0) API was another key result of the technical partnership between Google and Meta. It gives large-scale Jetpack Compose surfaces a consistent way to know when a composable is actually visible on screen, replacing custom, hand-rolled implementations used in the past. Within Instagram Direct alone, these visibility signals are used across hundreds of files to support product quality metrics that depend on whether UI elements were actually shown to people.
Startup performance
The adoption of Jetpack Compose for Instagram Direct led to unexpected performance improvements across other surfaces of the app. The Jetpack Compose runtime carries a warmup cost you pay only once, and because messaging is a high-traffic surface often visited early in a user session, other surfaces across Instagram that rely on Compose saw noticeable performance improvements.
The startup performance of Compose UI inside Instagram Direct itself was optimized through using Baseline Profiles, which pre-compile hot code paths at install time so Compose renders quickly from the very first launch.
Lessons from the Instagram Direct migration to Jetpack Compose
- Jetpack Compose has an immediate return on investment: You do not need to be using advanced AI workflows to benefit from Compose. With the ~50% reduction in code, it means less code to maintain, and reduced surface area for bugs.
- Designing an AI-native architecture led to significant wins, including a 35% reduction in AI agent execution time, 32% fewer engineer-agent exchanges, and a 33% reduction in token cost.
- Although there are plenty of interop APIs and support for combining Views and Compose together, aim to migrate bigger surfaces over individual small components. This keeps the UI within a single, uninterrupted composition hierarchy and unlocks all the best Compose-native performance optimizations.
- Pair Pausable composition with LazyLayoutCacheWindow: Pairing these two together yields better results than cache windows alone. With only the cache window, a heavy item could still try to compose in a single pass, potentially overrunning the frame budget.
- Contribute to Compose itself! Meta has partnered with the Jetpack Compose team to bring their feedback and ideas to life within Compose. Working on an open-source toolkit means we all benefit when bugs and performance improvements are made centrally. So, make your feedback known!
Adopting Jetpack Compose unlocked significant gains in AI-assisted development while simplifying day-to-day UI engineering at Instagram. The declarative approach reduces boilerplate, makes state easier to reason about, and improves overall developer productivity. The Instagram engineering team is looking forward to bringing Compose to more surfaces across the app, and the continued collaboration between Google and Meta to bring more improvements to Instagram and Jetpack Compose users alike.
If you haven't yet tried out Compose, now with AI-assistance, migrating to Jetpack Compose is easier than ever.
Acknowledgements. Thank you to Michal Zielinski and Matthew Du from Meta, and Andrei Shikov and George Mount from Google, for their work bringing performance improvements to Compose through the collaboration between Meta and Google! Thank you also to Gary Ye from Meta for helping bring Compose to Instagram Direct, and to Gopal Juneja from Meta for supporting this effort through data science!
30 Sep 2026 7:00pm GMT
29 Sep 2026
Android Developers Blog
Driving growth on Google Play: The next era of subscriptions

The subscription landscape is evolving rapidly, especially with the surge of generative AI and increasingly sophisticated app experiences. As the ecosystem shifts, we recognize that developers need more flexible and robust tools to monetize effectively while improving the LTV of recurring purchases. On Google Play, we are continuously expanding our subscription platform to help you drive growth, adapt to new business models, and meet your users exactly where they are.
Here is a look at the capabilities we are testing and rolling out to support the next generation of subscriptions, along with powerful existing features designed to maximize your conversion and retention.
Unlock new ways to sell and grow with flexible monetization models
As we look at the next few years of the subscription business, flexibility is paramount. Developers building GenAI tools, entertainment, educational platforms, and business solutions need adaptable pricing and packaging models to scale access beyond the individual user and capture higher cart value at checkout.
Multi-Quantity Subscriptions: Scale subscriptions to teams
To support collaborative and team-wide or group usage, Play is introducing Multi-Quantity Subscription Purchase. This allows users to make multiple subscription purchases in a single transaction and easily assign those as seats or subscriptions to team members or students. This is a game-changer for productivity, EdTech, and GenAI developers looking to sell team-wide subscription access seamlessly.
Usage-Based Billing: Support AI and variable-cost features
For apps with variable computing costs-like AI generation tools or other usage-based services-rigid recurring subscriptions do not always fit. Usage-Based Billing enables you to set up prepaid metered billing where users can automatically top up their balance whenever it falls below a set threshold. This ensures uninterrupted service for your users while protecting your margins.
Beyond these flexible models, we are also making it easier to package your products and upsell creatively at checkout:
Mixed Carts: Sell subscriptions and one-time products in a single checkout
Historically, subscriptions and one-time products were purchased in separate transactions. If a user wanted to buy a monthly membership alongside a starter pack of in-app currency or bonus credits, they had to complete two separate checkout flows.
Mixed Carts bridges this gap for developers who want to sell both auto-renewing subscriptions and one-time products (OTPs). By enabling you to process an auto-renewing base subscription alongside OTPs in a single API call and unified checkout sheet, Mixed Carts streamlines the transaction process.
This unified experience also opens up powerful upsell opportunities for your business-such as offering targeted discounts if an end user purchases a complete bundle of a subscription and complementary in-app items together.
Cross-Developer Bundling: Partner across apps to unlock shared growth
Partnerships are a proven strategy for acquiring new users and driving growth. With Cross-Developer Bundling, you can create and sell a hard bundle of two or more complementary subscriptions in your own catalog.
This capability allows you to team up with other developers-or combine offerings across your own portfolio of apps-to deliver massive value through a single purchase. For example, if you manage a language learning app, you can now create a single SKU that bundles your monthly membership with a partner's premium travel guide subscription, offering users a combined subscription at a discounted rate.
By sharing the acquisition benefits, you can seamlessly reach new audiences and secure more recurring revenue for your business.
Maximize subscription performance: Keep and win back the users you've earned
Acquiring a subscriber is only the first step-long-term growth depends on minimizing friction across the billing lifecycle. We are heavily invested in improving subscription performance to help you prevent involuntary payment declines and retain your subscribers.

The In-App Messaging API: Resolve payment declines and price change updates in-context
Available now to all developers, we highly encourage adopting the In-App Messaging API. This tool allows you to meet end users exactly where they are-inside your app-with critical transactional messages. You can use this API to:
- Prompt users to fix a payment decline immediately.
- Notify users of upcoming price changes transparently.
By handling these critical account states gracefully within the app experience, you can continue running your business without disrupting the user journey. Learn more.
Dynamic Grace Period: Tailor payment recovery windows with predictive models
Involuntary churn from payment declines is often addressed with a static, one-size-fits-all grace period. However, fixed durations force a difficult trade-off between giving users enough time to resolve payment issues and managing developer service costs during unpaid periods. With Dynamic Grace Period, Google Play utilizes machine learning and heuristic models to tailor the grace period duration for individual subscribers following a payment decline. By intelligently matching the recovery window to the user's recovery likelihood, this capability is designed to help developers better balance renewal recovery against unpaid service access. To ensure consistency with your business rules, Google Play automatically adjusts the subsequent account hold duration, preserving your total configured recovery window without requiring client-side code changes.
Retention Offers and Plan Change: Prevent voluntary churn in the cancellation flow
Acquiring new subscribers is expensive, making it critical to engage and retain your existing user base. When users consider canceling, capturing their attention before they leave is essential for protecting your customer lifetime value. With Retention Offers, you can present developer-funded incentives-like a discount-directly within the Play Store cancellation flow.
For users who may not be eligible for a discount or promotional offer, you can suggest a Plan Change to a lower-priced tier, ensuring you offer a flexible path to keep them engaged in your app rather than losing them entirely.
Native Winback Offers: Re-engage lapsed subscribers directly on the Play Store
A canceled subscription doesn't have to be the end of the user lifecycle. Former subscribers already understand the value of your app-they often just need the right incentive at the perfect moment to return. Traditional winback campaigns rely on email or push notifications, which fall flat if a user has uninstalled your app. Google Play's Subscription Winback Offers close this gap in your re-acquisition strategy by reaching users directly on the Google Play Store, helping you present lapsed users with personalized offers that make coming back easier than ever.
Behind the scenes: The revenue shield you don't have to build
Alongside the tools you configure in Play Console, Google Play runs a continuous engine of zero-lift optimizations behind the scenes to grow your subscriber base and reduce involuntary churn-without requiring a single line of developer code. From smart payment retries and automatically cycling through backup payment methods for opted-in users, to sending intelligent, context-aware reminders during grace periods and account hold, Play works continuously to recover failed transactions seamlessly.
We also protect your revenue with built-in fraud and abuse prevention systems that block bad actors from exploiting promotional offers or manipulating billing cycles. This ensures your promotional budgets reward legitimate, high-value subscribers-securing your business while naturally lifting overall retention.
Many of these features are currently available or rolling out through our Early Access Program, meaning capabilities are in active testing with select partners to gather feedback before rolling out more broadly in Play Console. If you work with a Google Play partner manager, you can reach out to express interest as programs open. To learn more about our current subscription capabilities and get your app ready for what's next, explore our Google Play Billing subscriptions documentation.
29 Sep 2026 4:00pm GMT
29 Aug 2026
Planet Maemo
An In-Depth Look at the LiberNovo Omni SE
Two months, one chair, and a cushion that had to be replaced - twice. This isn't a review in the strict sense. The only other chair I've used long-term is an IKEA Nominell, so I can't offer broad comparisons. What I can offer is an honest account of living with the Omni SE - plus a few observations most reviews skip.
Where I'm coming from
My background shapes what I look for in a chair, so it's worth stating up front.
I never had back problems with my old IKEA chair, but I credit that to regular exercise rather than to the chair. In my experience, exercise is by far the most effective way to prevent back pain - closely followed by changing posture throughout the day: standing up now and then and putting your leg muscles to work.
The upshot: for me, the specific chair matters less than people assume. What I value instead are quality-of-life features - freedom to move and the ability to recline deeply for a quick moment of relaxation. That's the lens I judged the Omni SE through.
The Omni chairs hardly need an introduction; they're among the most aggressively marketed chairs on social media. So rather than rehash the spec sheet (Dan Ahn's YouTube channel covers that well), I'll stick to my own experience.
Ordering
I paid the 10€ deposit, ordered on 16 June for 589€, and received the chair on 23 June - so about two months of use at the time of writing.
The 10€ "deposit" turned out to be a one-year warranty extension rather than a deposit. Fine by me.
The seat cushion problem
The downside of ordering early and cheap: at least the first batch shipped with foam that was too soft in the seat cushion.
The Omni cushion combines three foam densities, with the firmest section only near the backrest. Even that section wasn't firm enough - once the foam warmed up, you'd sink through to the plastic pan. Steve, the Anthros CEO, summarises the issue neatly - keep in mind though, that this is coming from a rival.
If you're reading this in a seemingly fine Omni and wonder what this feels like, sit on the front edge for a few minutes: the softer foam lets you bottom out. Sit with your lumbar against the backrest and you're on the firm section, where it should feel comfortable.
Customer support assured me that batches produced after early May went through improved firmness testing - which means my chair was made before that. At the time of writing, it's still unclear whether all new deliveries ship with the updated cushion. So if you'd rather not go through the hassle of getting a replacement, wait until that's confirmed.
How support handled it
Sitting with my lumbar against the backrest was exactly where the problem showed up for me, so after a few uncomfortable weeks I contacted support via email. They replied within 24 hours, and their first suggestion was that I should sit closer to the backrest. Once I confirmed I already was, they promised a revised cushion - and noted that my return window would restart on the day the replacement arrived.
A week later a new cushion arrived - the Pro version, sent by mistake. Since the correct replacement was another month out, I used the Pro cushion in the meantime; its firmer foam didn't bottom out. I also received a small compensation package for the inconvenience, including the StepSync Mat, which turned out to be surprisingly handy - more on that below.
The correct SE cushion arrived at the end of August, so at the time of writing I've used the Pro cushion for about a month and the revised SE cushion for three days.
On the two replacement cushions
The revised SE cushion is noticeably stiffer than the one my chair originally shipped with, across all three foam zones.
The Pro uses Gabriel Atlantic fabric, which the active ventilation requires because it allows more airflow. It's also more durable. The trade-off is the coarser weave: less soft and slightly scratchy compared to the standard fabric.
In terms of firmness, the two replacement cushions feel about the same to me - the fabric is the main difference. Upgrading to the Pro just for the fabric isn't worth it in my view.
Backrest and recline
The backrest is what LiberNovo builds its marketing around, and deservedly so. Sitting down for the first time, it was the most noticeable difference to my IKEA chair: it hugs you around the lumbar region and you immediately feel supported, without limiting your range of motion.
The second standout is the recline. If you've ever wanted to lie back for a moment of relaxation and quick back relief, that's what the 160° position gives you. Even at maximum recline the chair feels stable - but to be genuinely comfortable, you'll want to raise your legs. Whether that calls for the official footrest is up to you; I put the StepSync Mat on the subwoofer under my desk and called it a day.
Day to day, though, I keep the chair locked at 135° with the tension tightened up - that lets me move freely back and forth while still getting the back support.
Armrests
These get called out as a weak point, so I'll say plainly that I like them. Yes, they slide forward and backward very easily - but for me that's an advantage: I can pull up to the desk and the armrests simply move out of the way. If you prefer them to stay put, I can see it being annoying.
The one real drawback: at the two narrowest width settings they collide with the backrest unless fully extended forward.
Material-wise, the firmness and smooth surface are pleasant. Keep in mind, though, that my old chair had no armrests at all.
The standout: parts availability
The cushion swap points to what I consider LiberNovo's real strength - not the chair itself, but what you can do with it after you buy it.
Competing brands may advertise a 12-year warranty instead of six, but their spare-parts selection is very limited. With LiberNovo you can order every individual component - and, unusually, at fair prices: building an Omni SE from parts comes to €681, against €679 for the finished chair (current price). Most brands price their spares steeply enough to make that comparison absurd.
What that enables is customization: want a headrest in a different colour? Order that spare part. Worried about the motorized lumbar? Swap in the manual one from the SE.
Verdict
After two months, I'm happy with the upgrade. The Omni SE is far more comfortable than my IKEA chair, and I use the recline often - reading a book, or just taking the load off my back.
The biggest drawback is probably the fixed seat depth. You can change it later by buying the 45 cm / 48 cm cushion replacement, but that still doesn't let different people share the same chair.
LiberNovo recently extended the warranty to six years for everyone, which makes seven with my deposit. Since the SE has no electronic components, that covers the whole chair in my case.
My cushion wasn't right out of the box, and it still took two months and two shipments to sort out. But the parts availability and the support response mean you're not stuck with a problem you can't fix - and that, more than any spec, is what won me over.
I'll update this post if anything changes.
29 Aug 2026 11:27am GMT
04 Jul 2026
Planet Maemo
Reticulum is interesting
It all started innocently enough: sometime last summer, I ran into the blog post Start your own Internet Resiliency Club on Hacker News.
…communicate with each other across a few kilometers without any centralized infrastructure using cheap, low-power, unlicensed LoRa radios and open source Meshtastic text messaging software.
The idea of a local, infrastructure-free communications mesh sounded useful, especially as we were about to sail into the Pacific.
Meshtastic
While conflicts and natural disasters are hopefully far away, on the smaller atolls there is no cellular network. With Meshtastic we could communicate over LoRa.

Over the hurricane season, the Meshtastic setup became quite extensive. Our boat has a Meshtastic node, plus a mast-mounted solar repeater. We both have Meshtastic cards that we carry with us. With these we can communicate with text messages over quite a long distance. And we get telemetry and alerts from the boat.
In Cartagena, Colombia we could hear the boat pretty much across the city. And since some of our buddy boats also run Meshtastic, we've even had conversations while offshore.
While the existing Meshtastic setup is serving us well, there is always room for improvement and new ideas.
Reticulum
Reticulum is a project that seeks to take this to a whole new level. It is a whole decentralized networking stack that allows anything from instant messaging and voice calls to full-on SSH sessions to be carried over a multitude of different interfaces. You can transport Reticulum over LoRa, Bluetooth, and also over regular TCP/IP networks. And if authorities didn't take a dim view on encryption in ham radio, it would also work over our HF radio. With store-and-forward mechanisms it can deal with intermittent connectivity.
Because your identity is portable, your connectivity can be fluid. You can be sitting at a desk connected to a fiber backbone one moment, and walking through a field connected only to a long-range LoRa mesh the next. To the rest of the network, nothing has changed. Your friends do not need to update your contact info. The messages they send do not bounce back. The network senses the shift in the medium and reroutes the flow of data automatically.
You are no longer a stationary node in a fixed grid. You are a wanderer in a fluid medium.
- The Zen of Reticulum
As it stands now, Reticulum is still quite an early system with rudimentary and tech-heavy user interfaces. But that seems to be about to change: the Columba app for Android seems about as user-friendly as Meshtastic or something like Signal. There's a lot of potential in that once it reaches a stable version.
Distributed development over Reticulum
In the meanwhile, there is one aspect of Reticulum we developers can benefit from immediately: Distributed development. With it, any rngit node running on Reticulum can be your "GitHub". Git history, issue tracking, release distribution is already there.
I recently switched my various programming projects over. We have rngit running on the boat NAS, and VPS running a mirror behind more consistent connectivity. And for now I also mirror the work periodically to GitHub for backwards compatibility.
Reticulum for software
What I think is worthwhile to explore is having machines interface with Reticulum. Just like we can tell our boat to switch lights on via a Meshtastic message, we should be able to do the same with Reticulum. And maybe there should be a NomadNet "site" for the boat showing status of the various systems.
Going further, maybe boats could share chart data, depth soundings, weather information with each other over this. The promise of VDES, but built from the grassroots perspective.
And maybe things like NoFlo should be able to communicate over Reticulum? Reticulum implementations exist for multiple programming languages, but for this we'd need a JavaScript port.
There's still a lot to study and to think about. Watch this space. Last time I noted that something is interesting, it took me to a ten year rabbit hole.
04 Jul 2026 12:00am GMT
26 Jan 2026
Planet Maemo
Igalia Multimedia contributions in 2025
Now that 2025 is over, it's time to look back and feel proud of the path we've walked. Last year has been really exciting in terms of contributions to GStreamer and WebKit for the Igalia Multimedia team.
With more than 459 contributions along the year, we've been one of the top contributors to the GStreamer project, in areas like Vulkan Video, GstValidate, VA, GStreamer Editing Services, WebRTC or H.266 support.
In Vulkan Video we've worked on the VP9 video decoder, and cooperated with other contributors to push the AV1 decoder as well. There's now an H.264 base class for video encoding that is designed to support general hardware-accelerated processing.
GStreaming Editing Services, the framework to build video editing applications, has gained time remapping support, which now allows to include fast/slow motion effects in the videos. Video transformations (scaling, cropping, rounded corners, etc) are now hardware-accelerated thanks to the addition of new Skia-based GStreamer elements and integration with OpenGL. Buffer pool tuning and pipeline improvements have helped to optimize memory usage and performance, enabling the edition of 4K video at 60 frames per second. Much of this work to improve and ensure quality in GStreamer Editing Services has also brought improvements in the GstValidate testing framework, which will be useful for other parts of GStreamer.
Regarding H.266 (VVC), full playback support (with decoders such as vvdec and avdec_h266, demuxers and muxers for Matroska, MP4 and TS, and parsers for the vvc1 and vvi1 formats) is now available in GStreamer 1.26 thanks to Igalia's work. This allows user applications such as the WebKitGTK web browser to leverage the hardware accelerated decoding provided by VAAPI to play H.266 video using GStreamer.
Igalia has also been one of the top contributors to GStreamer Rust, with 43 contributions. Most of the commits there have been related to Vulkan Video.
In addition to GStreamer, the team also has a strong presence in WebKit, where we leverage our GStreamer knowledge to implement many features of the web engine related to multimedia. From the 1739 contributions to the WebKit project done last year by Igalia, the Multimedia team has made 323 of them. Nearly one third of those have been related to generic multimedia playback, and the rest have been on areas such as WebRTC, MediaStream, MSE, WebAudio, a new Quirks system to provide adaptations for specific hardware multimedia platforms at runtime, WebCodecs or MediaRecorder.
We're happy about what we've achieved along the year and look forward to maintaining this success and bringing even more exciting features and contributions in 2026.
26 Jan 2026 9:34am GMT
18 Sep 2022
Planet Openmoko
Harald "LaF0rge" Welte: Deployment of future community TDMoIP hub
I've mentioned some of my various retronetworking projects in some past blog posts. One of those projects is Osmocom Community TDM over IP (OCTOI). During the past 5 or so months, we have been using a number of GPS-synchronized open source icE1usb interconnected by a new, efficient but strill transparent TDMoIP protocol in order to run a distributed TDM/PDH network. This network is currently only used to provide ISDN services to retronetworking enthusiasts, but other uses like frame relay have also been validated.
So far, the central hub of this OCTOI network has been operating in the basement of my home, behind a consumer-grade DOCSIS cable modem connection. Given that TDMoIP is relatively sensitive to packet loss, this has been sub-optimal.
Luckily some of my old friends at noris.net have agreed to host a new OCTOI hub free of charge in one of their ultra-reliable co-location data centres. I'm already hosting some other machines there for 20+ years, and noris.net is a good fit given that they were - in their early days as an ISP - the driving force in the early 90s behind one of the Linux kernel ISDN stracks called u-isdn. So after many decades, ISDN returns to them in a very different way.
Side note: In case you're curious, a reconstructed partial release history of the u-isdn code can be found on gitea.osmocom.org
But I digress. So today, there was the installation of this new OCTOI hub setup. It has been prepared for several weeks in advance, and the hub contains two circuit boards designed entirely only for this use case. The most difficult challenge was the fact that this data centre has no existing GPS RF distribution, and the roof is ~ 100m of CAT5 cable (no fiber!) away from the roof. So we faced the challenge of passing the 1PPS (1 pulse per second) signal reliably through several steps of lightning/over-voltage protection into the icE1usb whose internal GPS-DO serves as a grandmaster clock for the TDM network.
The equipment deployed in this installation currently contains:
-
a rather beefy Supermicro 2U server with EPYC 7113P CPU and 4x PCIe, two of which are populated with Digium TE820 cards resulting in a total of 16 E1 ports
-
an icE1usb with RS422 interface board connected via 100m RS422 to an Ericsson GPS03 receiver. There's two layers of of over-voltage protection on the RS422 (each with gas discharge tubes and TVS) and two stages of over-voltage protection in the coaxial cable between antenna and GPS receiver.
-
a Livingston Portmaster3 RAS server
-
a Cisco AS5400 RAS server
For more details, see this wiki page and this ticket
Now that the physical deployment has been made, the next steps will be to migrate all the TDMoIP links from the existing user base over to the new hub. We hope the reliability and performance will be much better than behind DOCSIS.
In any case, this new setup for sure has a lot of capacity to connect many more more users to this network. At this point we can still only offer E1 PRI interfaces. I expect that at some point during the coming winter the project for remote TDMoIP BRI (S/T, S0-Bus) connectivity will become available.
Acknowledgements
I'd like to thank anyone helping this effort, specifically * Sylvain "tnt" Munaut for his work on the RS422 interface board (+ gateware/firmware) * noris.net for sponsoring the co-location * sysmocom for sponsoring the EPYC server hardware
18 Sep 2022 10:00pm GMT
08 Sep 2022
Planet Openmoko
Harald "LaF0rge" Welte: Progress on the ITU-T V5 access network front
Almost one year after my post regarding first steps towards a V5 implementation, some friends and I were finally able to visit Wobcom, a small German city carrier and pick up a lot of decommissioned POTS/ISDN/PDH/SDH equipment, primarily V5 access networks.
This means that a number of retronetworking enthusiasts now have a chance to play with Siemens Fastlink, Nokia EKSOS and DeTeWe ALIAN access networks/multiplexers.
My primary interest is in Nokia EKSOS, which looks like an rather easy, low-complexity target. As one of the first steps, I took PCB photographs of the various modules/cards in the shelf, take note of the main chip designations and started to search for the related data sheets.
The results can be found in the Osmocom retronetworking wiki, with https://osmocom.org/projects/retronetworking/wiki/Nokia_EKSOS being the main entry page, and sub-pages about
In short: Unsurprisingly, a lot of Infineon analog and digital ICs for the POTS and ISDN ports, as well as a number of Motorola M68k based QUICC32 microprocessors and several unknown ASICs.
So with V5 hardware at my disposal, I've slowly re-started my efforts to implement the LE (local exchange) side of the V5 protocol stack, with the goal of eventually being able to interface those V5 AN with the Osmocom Community TDM over IP network. Once that is in place, we should also be able to offer real ISDN Uk0 (BRI) and POTS lines at retrocomputing events or hacker camps in the coming years.
08 Sep 2022 10:00pm GMT
Harald "LaF0rge" Welte: Clock sync trouble with Digium cards and timing cables
If you have ever worked with Digium (now part of Sangoma) digital telephony interface cards such as the TE110/410/420/820 (single to octal E1/T1/J1 PRI cards), you will probably have seen that they always have a timing connector, where the timing information can be passed from one card to another.
In PDH/ISDN (or even SDH) networks, it is very important to have a synchronized clock across the network. If the clocks are drifting, there will be underruns or overruns, with associated phase jumps that are particularly dangerous when analog modem calls are transported.
In traditional ISDN use cases, the clock is always provided by the network operator, and any customer/user side equipment is expected to synchronize to that clock.
So this Digium timing cable is needed in applications where you have more PRI lines than possible with one card, but only a subset of your lines (spans) are connected to the public operator. The timing cable should make sure that the clock received on one port from the public operator should be used as transmit bit-clock on all of the other ports, no matter on which card.
Unfortunately this decades-old Digium timing cable approach seems to suffer from some problems.
bursty bit clock changes until link is up
The first problem is that downstream port transmit bit clock was jumping around in bursts every two or so seconds. You can see an oscillogram of the E1 master signal (yellow) received by one TE820 card and the transmit of the slave ports on the other card at https://people.osmocom.org/laforge/photos/te820_timingcable_problem.mp4
As you can see, for some seconds the two clocks seem to be in perfect lock/sync, but in between there are periods of immense clock drift.
What I'd have expected is the behavior that can be seen at https://people.osmocom.org/laforge/photos/te820_notimingcable_loopback.mp4 - which shows a similar setup but without the use of a timing cable: Both the master clock input and the clock output were connected on the same TE820 card.
As I found out much later, this problem only occurs until any of the downstream/slave ports is fully OK/GREEN.
This is surprising, as any other E1 equipment I've seen always transmits at a constant bit clock irrespective whether there's any signal in the opposite direction, and irrespective of whether any other ports are up/aligned or not.
But ok, once you adjust your expectations to this Digium peculiarity, you can actually proceed.
clock drift between master and slave cards
Once any of the spans of a slave card on the timing bus are fully aligned, the transmit bit clocks of all of its ports appear to be in sync/lock - yay - but unfortunately only at the very first glance.
When looking at it for more than a few seconds, one can see a slow, continuous drift of the slave bit clocks compared to the master :(
Some initial measurements show that the clock of the slave card of the timing cable is drifting at about 12.5 ppb (parts per billion) when compared against the master clock reference.
This is rather disappointing, given that the whole point of a timing cable is to ensure you have one reference clock with all signals locked to it.
The work-around
If you are willing to sacrifice one port (span) of each card, you can work around that slow-clock-drift issue by connecting an external loopback cable. So the master card is configured to use the clock provided by the upstream provider. Its other ports (spans) will transmit at the exact recovered clock rate with no drift. You can use any of those ports to provide the clock reference to a port on the slave card using an external loopback cable.
In this setup, your slave card[s] will have perfect bit clock sync/lock.
Its just rather sad that you need to sacrifice ports just for achieving proper clock sync - something that the timing connectors and cables claim to do, but in reality don't achieve, at least not in my setup with the most modern and high-end octal-port PCIe cards (TE820).
08 Sep 2022 10:00pm GMT










